Monthly Archives: November 2015

Rotations

Two further nights I have spent the re-engineering of the coordinate transformation of rotated graphical objects. Oracle Forms allows it to rotate rectangle or lines with a defined angle. The angle can be integers between 0 and 359 degree.

Actually I haven’t any interest in graphical transformations, simple moves of objects can program everybody, but to calculate new coordinates for a rotated object is more complex.

The good news are, the FMX file contains the angle value. So I can calculate the new coordinates with a little bit mathematics.

I have started with rectangles, four corners, should be possible. As I entered a rotation angle, there were a lot of changes within the FMX file. I found the rotation angle, but why there are other changes? Some of the changes I had already located as x, y, width and height of the rectangle, but the meaning has been changed with a rotating angle != 0. To find out, what is going on in the file, I have to go deeper into mathematics.

Oracle Forms rotates the rectangle around its center. Every corner will get new coordinates. So the first thing I need is the center of the rectangle. It is simple to calculate for a non-rotated rectangle:

cx = x + (width/2)
cy = y + (height/2)

Next, I calculate every corner coordinate:

x0 = x, y0 = y
x1 = x + width, y1 = y
x2 = x + width, y + height
x3 = x, y + height

These four coordinates I have to transform by the following formula, where (x,y) is the corner coordinate, (cx,cy) is the center coordinate, q is the rotation angle and (x’,y’) is the new coordinate:

x' = x1 + cos(q) * (x - cx) - sin(q) * (y - cy)
y' = y1 + sin(q) * (x - cx) + cos(q) * (y - cy)

With a little bit Java, I can describe it as:

package info.phosco.forms.rotate;

public class OraclePoint {

	private final double x;
	private final double y;

	public OraclePoint(double x, double y) {
		this.x = Math.round(x * 1000.0) / 1000.0;
		this.y = Math.round(y * 1000.0) / 1000.0;
	}

	public OraclePoint(int x, int y) {
		this((double) x, (double) y);
	}

	public double getX() {
		return this.x;
	}

	public double getY() {
		return this.y;
	}

	@Override
	public String toString() {
		return getClass().getName() + "[" + this.x + ", " + this.y + "]";
	}

}
package info.phosco.forms.rotate;

public class RotateRect {

	// Rotation point: x1,y1
	// Original x,y
	// Rotated x',y'
	// Rotation angle q

	private static OraclePoint rotate(OraclePoint p, OraclePoint c, int angle) {
		// x' = x1 + cos(q) * (x - x1) - sin(q) * (y - y1)
		double x = c.getX() + Math.cos(Math.toRadians(angle)) * (p.getX() - c.getX()) - Math.sin(Math.toRadians(angle))
				* (p.getY() - c.getY());

		// y' = y1 + sin(q) * (x - x1) + cos(q) * (y - y1)
		double y = c.getY() + Math.sin(Math.toRadians(angle)) * (p.getX() - c.getX()) - Math.cos(Math.toRadians(angle))
				* (p.getY() - c.getY());

		return new OraclePoint(x, y);
	}

	public static void main(String args[]) {
		int x = 37;
		int y = 47;
		int w = 120;
		int h = 73;
		int angle = 45;

		// calc every corner
		OraclePoint p0 = new OraclePoint(x, y);
		OraclePoint p1 = new OraclePoint(x + w, y);
		OraclePoint p2 = new OraclePoint(x + w, y + h);
		OraclePoint p3 = new OraclePoint(x, y + h);

		System.out.println("rotate angle: 0°");
		System.out.println(p0);
		System.out.println(p1);
		System.out.println(p2);
		System.out.println(p3);

		// calc center point
		OraclePoint center = new OraclePoint((double) x + ((double) w / 2.0), (double) y + ((double) h / 2.0));

		// Rotate rectangle
		OraclePoint r0 = rotate(p0, center, angle);
		OraclePoint r1 = rotate(p1, center, angle);
		OraclePoint r2 = rotate(p2, center, angle);
		OraclePoint r3 = rotate(p3, center, angle);

		System.out.println("----------");
		System.out.println("rotate angle: " + angle + "°");
		System.out.println(r0);
		System.out.println(r1);
		System.out.println(r2);
		System.out.println(r3);

	}
}

I can round the new coordinates a bit and it was possible to find the values (after a further transformation with the Oracle Form coordinate system factor) within the changes of the FMX file. Some changes I cannot explain, some cordinates seems to be double stored into the file.

Coordinate systems

An FMX file contains coordinates like x-pos and y-pos, width and height of components (canvas, buttons, graphical elements like rectangle or arc). The compiled format doesn’t store different values for every coordinate, if we change the coordinate system from pixels to dots or inches to centimeters. The numeric value is stable, there must be a factor, which differs for every unit.

Let’s take a look to the values of a rectangle. We have x, y, width and height (and we have the same tuple for the border description). The line thickness will be pre-calculated into coordinates.

X = 9102
Y = 4324
Width = 16270
Height = 5689

The coordinates in the Form Builder were (for coordinate system DOTs)

80
38
143
50

How I can translate the large values into the Dot coordinates? This question has engaged me for two days. First, the values aren’t Oracle Numbers, they have a stable size of 4 bytes. Next I have divided both values:

9102 / 80 = 113.775
4324 / 38 = 113.789
16270 / 143 = 113.776
5689 / 50 = 113.78

This seems to be a static result (except little rounding errors). If I switch to another coordinate system, I’ll get another static result (e.g. for PIXELS it will be 85,333). These values seem not to be a DPI, I haven’t seen such a value with a fraction part. But how can I calculate the values? In the Oracle documentation you can find a short sentence about the DOTs coordinate system: every dot is a 1/72 of an inch. But I cannot convert 1/72 into 113.something …

A lot of tests later I have seen a well-known value: a width coordinate for one inch has a value of 8192, ha! Let’s try:

9102 / 8192 = 1.1111 * 72 = 79.998
4324 / 8192 = 0.5278 * 72 = 38.004
16270 / 8192 = 1.9861 * 72 = 142.998
5689 / 8192 = 0.6944 * 72 = 50.000

Perfect match! I can use Math.round(float val) to get the right Form Builder coordinates. The other coordinate systems have some other factors (pixel = 96, centimeters = 2.54, inches = 1, decimal = 720). But you have always to use 8192 in the formula. I’ll use an Java enum to handle the translation:

public enum CoordSystemUnit {

	PIXEL(0x2) {
		public float convert(int coord) {
			float factor = (8192f / 1000f) / 96f;
			return Math.round(coord / factor) * 1000f;
		}
	},
	INCH(0x3) {
		public float convert(int coord) {
			float factor = (8192f / 1000f) / 1f;
			return Math.round(coord / factor) * 1000f;
		}
	},
	CENTIMETER(0x5) {
		public float convert(int coord) {
			float factor = (8192f / 1000f) / 2.54f;
			return Math.round(coord / factor) * 1000f;
		}
	},
	DOT(0x7) {
		public float convert(int coord) {
			float factor = (8192f / 1000f) / 72f;
			return Math.round(coord / factor) * 1000f;
		}
	},
	DECIMAL(0xC) {
		public float convert(int coord) {
			float factor = (8192f / 1000f) / 720f;
			return Math.round(coord / factor) * 1000f;
		}
	};

	private final int unit;

	private CoordSystemUnit(int unit) {
		this.unit = unit;
	}

	public static CoordSystemUnit lookup(int unit) {
		for (CoordSystemUnit t : CoordSystemUnit.values()) {
			if (t.unit == unit) {
				return t;
			}
		}
		throw new IllegalArgumentException("Unknown CoordSystemUnit " + unit);
	}

	public abstract float convert(int coord);
}

Oracle’s internal formats.3

Negative values between -1 and 0

A value of -0.00412
40 we can use 0x40 xor 0x7f = 0x3f (which is the same exponent as for the positive value)
3C we can use 0x65 (101) – 0x3C = 0x29 (41)
51 we can use 0x65 (101) – 0x51 = 0x14 (20)
66 the minus sign

A value of -0.412
3f xor 7f = 40 (positive equivalent is 0xC0, highest bit differs) = -2
3c should be 41
51 should be 20
66

Seems to work.

+/- infinity

There is a byte array

FF
65

which is used for numbers larger than 9.999..9*10^125.

Smaller values then -9.999..9*10^125 will be stored as a single byte

00

Both values could be interpreted as +/- infinity.

Borders

Values with a smaller exponent than -130 will be stored as zero (one byte 0x80).

I have written the algorithms into a Java class:

import java.math.BigDecimal;
import java.util.Arrays;

public class OracleNumber {

	private final BigDecimal value;

	public static final OracleNumber ZERO = new OracleNumber(BigDecimal.ZERO);
	public static final OracleNumber POSITIVE_INFINITY = new OracleNumber(new BigDecimal("1E200"));
	public static final OracleNumber NEGATIVE_INFINITY = new OracleNumber(new BigDecimal("-1E200"));

	private OracleNumber(BigDecimal val) {
		this.value = val;
	}

	private OracleNumber(boolean negative, String mantissa, int exponent) {
		this.value = new BigDecimal((negative ? "-" : "") + mantissa + "0E" + exponent).stripTrailingZeros();
	}

	private static boolean isZero(short[] array) {
		return array.length == 1 && array[0] == 0x80;
	}

	private static boolean isNegative(short[] array) {
		return ((array[0] & 0x80) == 0);
	}

	private static boolean hasPositiveExponent(short[] array) {
		return (array[0] & 0x40) != 0;
	}

	private static boolean isPositiveInfinity(short[] array) {
		return (array.length == 2 && array[0] == 0xFF && array[1] == 0x65);
	}

	private static boolean isNegativeInfinity(short[] array) {
		return (array.length == 1 && array[0] == 0x0);
	}

	public static OracleNumber newInstance(short[] array) {

		short[] number = Arrays.copyOf(array, array.length);

		if (isZero(number)) {
			return OracleNumber.ZERO;
		}

		if (isNegativeInfinity(number)) {
			return OracleNumber.NEGATIVE_INFINITY;
		}

		if (isPositiveInfinity(number)) {
			return OracleNumber.POSITIVE_INFINITY;
		}

		if (isNegative(number)) {
			// convert exponent into the equivalent for positive numbers
			number[0] = (short) ((number[0] | 0x80) ^ 0x7F);
		}

		int exponent;

		if (hasPositiveExponent(number)) {
			exponent = ((number[0] & 0x3F) << 1) - 2;
		} else {
			exponent = -1 * (((number[0] ^ 0xBF) << 1) + 4);
		}

		// calculating mantissa
		String mantissa = "";
		for (int i = 1; i < number.length; i++) {
			if (isNegative(array)) {
				mantissa += (number[i] > 0x65) ? "" : (0x65 - number[i]);
			} else {
				mantissa += (number[i] - 1);
			}
			if (i == 1) {
				mantissa += ".";
			}
		}

		return new OracleNumber(isNegative(array), mantissa, exponent);
	}

	@Override
	public String toString() {
		return value.toString();
	}

	public BigDecimal getValue() {
		return new BigDecimal(value.toString());
	}
}

Oracle’s internal formats.2

Negative values

A value of -99.99
3e = 0011 1110, this should 10^0 = 1
02 = 0000 0010
02 = 0000 0010
66 = 0110 0110 (seems to be the minus sign)

A value of -98.98
3e = 0011 1110, this should 10^0 = 1
03 = we could use 101 (0x65) as base – 3 = 98
03 = same
66 = minus sign

A value of -412
3d = 0011 1101, this should 10^2 = 100
61 = 0110 0001 = 0x65 – 0x61 = 0x4
59 = 0101 1001 = 0x65 – 0x59 = 0xC (12)
66 = 0110 0110 (minus sign)

A value of -1
3e = 10^0
64 = 0x65 – 0x64 = 1
66 = minus sign

I can transform the exponent by

exponent xor 0x7F

which results in an exponent as for positive numbers. The last mantissa byte is always 0x66, which we can ignore (minus sign). The exponent contains in the highest bit the sign flag, so I can differ between positive and negative numbers by

exponent & 0x80 = 0 (negative) = 1 (positive)

Negative values less than 1

A value of -0.00412
40
3C
51
66

WTF?

Oracle’s internal formats.1

The Oracle database documentation is really helpful for the decompiler 🙂 There are a lot of information about the internal storage of datatypes (DATE and NUMBER). You can search for “Oracle native datatypes”.

The FMX files contain a lot of such byte arrays, and it is really simple to translate these with such information. Currently I try to translate the values of static record groups, which can contain CHARACTER (simple strings), DATEs and NUMBERs.

Date

Oracle uses an internal format, which has 7 bytes: century, year, month, day_of_month, hour, minutes, seconds. In my example files, century and year are increased by a static value of 100. So you have an array:

78 = 120
73 = 115
04 = 4
0A = 10
01 = unused
01 = unused
01 = unused

for the 10-APR-2015 (without timestamp). I’ll see, how the time part is set for complete datetimes. The offset of 100 could differ between the BC and AD base.

Number

There is also a chapter about the internal numeric format on the same page in the Oracle documentation.

Oracle uses 1 byte to store the exponent and up to 20 bytes to store the mantissa.

For the value 100, there should be an exponent of 2 (10^2) and a mantissa of 1. But the byte array contains 2 bytes:

C2 = 1100 0010 = exponent of 2 and a bitmask in the upper half byte
02 = 0000 0010 = mantissa ?

For the value of 10, I get

C1 = 1100 0001 = exponent of 1 and the same bitmask
0B = 0000 1011 = mantissa ?

For the value of 1, I get

C1
02

For the value of 0, I get only one byte as described in the documentation:

80 = 1000 0000

For the value of 5, I get

C1 = 1100 0001
06 = 0000 0110

There seems to be a pattern to the value of 1, the mantissa should be reduced by 1 for the real value. It works also for the value 10, 0x0B minus 1 is 0x0A=10. Then I should also reduce the exponent by 1 because 10^1 * 10 = 100 and not 10 (but 10^0 = 1 * 10 = 10). But it doesn’t work for 100, 10^1 = 10 * 1 = 10 and not 100.

I could try a bitmask, exponent & 0x3E, which would remove the lowest bit and the upper 2 bits, if it set. The upper two bits could mark positive/negative values, I will see.

Value of 412, the example in the Oracle documentation:

C2 = 1100 0010 = exponent & 0x3E = 2 = 100
05 = 0000 0101 = mantissa of 5 (-1 = 4)
0D = 0000 1101 = mantissa of 13 (-1 = 12)

This seems to work, the exponent is 2 (=100), the mantissa is 4.12 * 100 = 412.

The question is, which value will switch to the next byte? 12 could also be stores as 1 and 2, but it is stored into one byte as 0C (+1 = 0D).

Value of 499:

C2 = exponent 2 = 100
05 = (5-1) = 4
64 = 0110 0100 = (100-1) = 99

Value of 4999:

C2 = exponent 2 = 100
32 = (50-1) = 49
64 = (100-1) = 99

So it seems that every byte can store values till 99. This will also match with the highest possible number (Oracle documentation) of 9.99…9 x 10^125.

Let us test some decimal numbers with some significant fractions.

Value of 412.56

c2 exponent = 2 = 100
05 mantissa 5-1 = 4
0d mantissa 13-1 = 12
39 mantissa 57-1 = 56

As expected.

Value of 0.00412

bf = 1011 1111 = 191?
2a = 0010 1010 = (42-1) = 41
15 = 0001 0101 = (21-1) = 20

Hm, the two mantissa bytes get 41.20, we would need an exponent of -4 to get the original value. Our exponent (0xbf) should contain a negative sign and the value of 4. Too heavy for me, let us use another value:

Value of 0.0412

c0
05 = (5-1) = 4
0d = (13-1) = 12

The mantissa is 4.12, we need an exponent of -2.

Value of 0.412

c0
2a = (42-1) = 41
15 = (21-1) = 20

The mantissa is 41.20, we need an exponent of -2 too.

So it seems, there is no exponent 10^-1 and -2 is encoded as 0xC0. The exponent 10^1 is also not used, because the first mantissa byte can hold values from 1..99.

What about 0.0000412?

be = 1011 1110
2a = (42-1) = 41
15 = (21-1) = 20

The exponent has been changed by 1, but it must store now -6 instead of 0xBF, which should be -4. So the exponent -5 doesn’t exist.

If we remove the highest bit and define the second bit as negative flag (0=negative, 1=positive), then we have values of 3F (-4) and 3E (-6).

The next exponent switch must be with 0.000000412:

bd = 1011 1101 = (3D = -8)
2a = 41
15 = 20

As algorithm to calculate the real negative exponent (if the second highest bit is 0) I could use

1. shift the bits by 1 to the left
2. add 1
3. negate the value
4. add 4

111111 XOR 0xbf << 1 = 0000000 0 + 4 = 4
111110 XOR 0xbf << 1 = 0000010 2 + 4 = 6
111101 XOR 0xbf << 1 = 0000100 4 + 4 = 8
111100 XOR 0xbf << 1 = 0000110 6 + 4 = 10

Seems to work, special case for exponent 0xC0 = -2.

Now there are some special numbers (NaN, +/-infinity) and also negative values left. But not today.

FMX.Decompiler.2

Ideas

I think, there are some structures within the file. Actually a file contains something like a header (magic number, version information) or a footer (description of the file structure), there must be references between the parts. A compiled file should contain lists, arrays, bitmasks etc. Strings can be null-terminated or there is a length information for every string. Repeatable structures must have a given length (in bytes or 32/64bit values). The only thing I need is an entry point to find more and more structures.

If the .fmx file contains descriptions, it should be possible to create simple .fmb files, convert it into .fmx and analyze the differences between a base file and a changed file (ie. change a foreground color, it should switch a few bytes in the .fmx). So I can investigate, which attributes change which bytes and generate a map of file content.

FMX.Decompiler.1

Currently I work on a decompiler for .fmx files, which are compiled Oracle Forms applications. Such applications can be built by Oracle Form Builder, which stores .fmb files (uncompiled) and .fmx files (compiled).

Why I need that? We use Oracle Forms since a lot of years (Forms6 ++) and we have thousends of commits within our Subversion for our Forms applications. But for some old applications we only have the .fmx files and we cannot find the associated .fmb. The application can still be used, but it is not possible to enhance or bugfix it. To rescue such applications, it would be helpful to get the definitions from the .fmx.

Search

Google returns only posts, where people need such a decompiler too, but nobody has a solution. Seems to be undiscovered country. Or, Oracle suppress such a knowledge. Hm, seems to be a home improvement project.

Idea

FMX files are used by the Oracle Forms Runtime but also by an Java based client/server architecture. So I think, the .fmx files contain the description of the application, but no executable code for a specific platform. So it should be possible to decompile it and restore the former .fmb.

Show it in Hex

I have load a simple .fmx file into a hex-editor. Currently I use DHex. The file contains plaintext structures like names for windows, canvases, recordgroups and so on. So the first try confirms my presumption, there are descriptions and no executable code.