Skip to content

Commit

Permalink
Extend Sony makernote handling
Browse files Browse the repository at this point in the history
Decodes the ciphered data stored within the 9050B tag into its own directory.
  • Loading branch information
drewnoakes committed May 17, 2022
1 parent 2fd3f66 commit ae792e7
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 8 deletions.
20 changes: 12 additions & 8 deletions Source/com/drew/metadata/exif/ExifTiffHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,12 @@ public boolean tryEnterSubIfd(int tagId)
pushDirectory(GpsDirectory.class);
return true;
}
}

if (_currentDirectory instanceof ExifSubIFDDirectory) {
} else if (_currentDirectory instanceof ExifSubIFDDirectory) {
if (tagId == ExifSubIFDDirectory.TAG_INTEROP_OFFSET) {
pushDirectory(ExifInteropDirectory.class);
return true;
}
}

if (_currentDirectory instanceof OlympusMakernoteDirectory) {
} else if (_currentDirectory instanceof OlympusMakernoteDirectory) {
// Note: these also appear in customProcessTag because some are IFD pointers while others begin immediately
// for the same directories
switch(tagId) {
Expand Down Expand Up @@ -336,6 +332,16 @@ public boolean customProcessTag(final int tagOffset,
}
}

if (_currentDirectory instanceof SonyType1MakernoteDirectory) {
if (tagId == SonyType1MakernoteDirectory.TAG_9050B) {
byte[] bytes = reader.getBytes(tagOffset, byteCount);
SonyTag9050bDirectory directory = SonyTag9050bDirectory.read(bytes);
directory.setParent(_currentDirectory);
_metadata.addDirectory(directory);
return true;
}
}

return false;
}

Expand Down Expand Up @@ -878,8 +884,6 @@ private static void processReconyxHyperFire2Makernote(@NotNull final ReconyxHype
// two unread bytes: the serial number's terminating null
}



private static void processReconyxUltraFireMakernote(@NotNull final ReconyxUltraFireMakernoteDirectory directory, final int makernoteOffset, @NotNull final RandomAccessReader reader) throws IOException
{
directory.setString(ReconyxUltraFireMakernoteDirectory.TAG_LABEL, reader.getString(makernoteOffset, 9, Charsets.UTF_8));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.drew.metadata.exif.makernotes;

import com.drew.metadata.Directory;

public abstract class SonyEncodedDataDirectoryBase extends Directory
{
private static final byte[] _substitution = new byte[]
{
0, 1, 50, (byte)177, 10, 14, (byte)135, 40, 2, (byte)204, (byte)202, (byte)173, 27, (byte)220, 8, (byte)237, 100,
(byte)134, (byte)240, 79, (byte)140, 108, (byte)184, (byte)203, 105, (byte)196, 44, 3, (byte)151, (byte)182,
(byte)147, 124, 20, (byte)243, (byte)226, 62, 48, (byte)142, (byte)215, 96, 28, (byte)161, (byte)171, 55,
(byte)236, 117, (byte)190, 35, 21, 106, 89, 63, (byte)208, (byte)185, (byte)150, (byte)181, 80, 39, (byte)136,
(byte)227, (byte)129, (byte)148, (byte)224, (byte)192, 4, 92, (byte)198, (byte)232, 95, 75, 112, 56, (byte)159,
(byte)130, (byte)128, 81, 43, (byte)197, 69, 73, (byte)155, 33, 82, 83, 84, (byte)133, 11, 93, 97, (byte)218,
123, 85, 38, 36, 7, 110, 54, 91, 71, (byte)183, (byte)217, 74, (byte)162, (byte)223, (byte)191, 18, 37,
(byte)188, 30, 127, 86, (byte)234, 16, (byte)230, (byte)207, 103, 77, 60, (byte)145, (byte)131, (byte)225, 49,
(byte)179, 111, (byte)244, 5, (byte)138, 70, (byte)200, 24, 118, 104, (byte)189, (byte)172, (byte)146, 42, 19,
(byte)233, 15, (byte)163, 122, (byte)219, 61, (byte)212, (byte)231, 58, 26, 87, (byte)175, 32, 66, (byte)178,
(byte)158, (byte)195, (byte)139, (byte)242, (byte)213, (byte)211, (byte)164, 126, 31, (byte)152, (byte)156,
(byte)238, 116, (byte)165, (byte)166, (byte)167, (byte)216, 94, (byte)176, (byte)180, 52, (byte)206, (byte)168,
121, 119, 90, (byte)193, (byte)137, (byte)174, (byte)154, 17, 51, (byte)157, (byte)245, 57, 25, 101, 120, 22,
113, (byte)210, (byte)169, 68, 99, 64, 41, (byte)186, (byte)160, (byte)143, (byte)228, (byte)214, 59, (byte)132,
13, (byte)194, 78, 88, (byte)221, (byte)153, 34, 107, (byte)201, (byte)187, 23, 6, (byte)229, 125, 102, 67, 98,
(byte)246, (byte)205, 53, (byte)144, 46, 65, (byte)141, 109, (byte)170, 9, 115, (byte)149, 12, (byte)241, 29,
(byte)222, 76, 47, 45, (byte)247, (byte)209, 114, (byte)235, (byte)239, 72, (byte)199, (byte)248, (byte)249,
(byte)250, (byte)251, (byte)252, (byte)253, (byte)254, (byte)255
};

protected static void decipherInPlace(byte[] bytes)
{
for (int i = 0; i < bytes.length; i++) {
bytes[i] = _substitution[bytes[i] & 0xFF];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.drew.metadata.exif.makernotes;

import com.drew.lang.annotations.NotNull;
import com.drew.lang.annotations.Nullable;
import com.drew.metadata.TagDescriptor;

public class SonyTag9050bDescriptor extends TagDescriptor<SonyTag9050bDirectory>
{
public SonyTag9050bDescriptor(@NotNull SonyTag9050bDirectory directory)
{
super(directory);
}

@Override
@Nullable
public String getDescription(int tagType)
{
switch (tagType) {
case SonyTag9050bDirectory.TAG_FLASH_STATUS:
return getFlashStatusDescription();
case SonyTag9050bDirectory.TAG_SONY_EXPOSURE_TIME:
return getSonyExposureTimeDescription();
case SonyTag9050bDirectory.TAG_INTERNAL_SERIAL_NUMBER:
return getInternalSerialNumberDescription();
default:
return super.getDescription(tagType);
}
}

@Nullable
public String getInternalSerialNumberDescription()
{
int[] values = _directory.getIntArray(SonyTag9050bDirectory.TAG_INTERNAL_SERIAL_NUMBER);
if (values == null)
return null;
StringBuilder sn = new StringBuilder();
for (int value : values) {
sn.append(String.format("%02x", value));
}
return sn.toString();
}

@Nullable
public String getSonyExposureTimeDescription()
{
Float value = _directory.getFloatObject(SonyTag9050bDirectory.TAG_SONY_EXPOSURE_TIME);
if (value == null)
return null;
if (value == 0)
return "0";
return String.format("1/%s", (int)(0.5 + (1 / value)));
}

@Nullable
public String getFlashStatusDescription()
{

Integer value = _directory.getInteger(SonyTag9050bDirectory.TAG_FLASH_STATUS);
if (value == null)
return null;
switch (value) {
case 0x00:
return "No flash present";
case 0x02:
return "Flash inhibited";
case 0x40:
return "Built-in flash present";
case 0x41:
return "Built-in flash fired";
case 0x42:
return "Built-in flash inhibited";
case 0x80:
return "External flash present";
case 0x81:
return "External flash fired";
default:
return "Unknown (" + value + ")";
}
}
}
162 changes: 162 additions & 0 deletions Source/com/drew/metadata/exif/makernotes/SonyTag9050bDirectory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.drew.metadata.exif.makernotes;

import com.drew.lang.ByteArrayReader;
import com.drew.lang.annotations.NotNull;

import java.io.IOException;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.HashMap;

public class SonyTag9050bDirectory extends SonyEncodedDataDirectoryBase
{
public static final int TAG_SHUTTER = 0x0026;
public static final int TAG_FLASH_STATUS = 0x0039;
public static final int TAG_SHUTTER_COUNT = 0x003a;
public static final int TAG_SONY_EXPOSURE_TIME = 0x0046;
public static final int TAG_SONY_F_NUMBER = 0x0048;
public static final int TAG_RELEASE_MODE_2 = 0x006d;
public static final int TAG_INTERNAL_SERIAL_NUMBER = 0x0088;
public static final int TAG_LENS_MOUNT = 0x0105;
public static final int TAG_LENS_FORMAT = 0x0106;
public static final int TAG_LENS_TYPE_2 = 0x0107;
public static final int TAG_DISTORTION_CORR_PARAMS_PRESENT = 0x010b;
public static final int TAG_APS_C_SIZE_CAPTURE = 0x0114;
public static final int TAG_LENS_SPEC_FEATURES = 0x0116;
public static final int TAG_SHUTTER_COUNT_3 = 0x019f;

@NotNull
private static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();

static {
_tagNameMap.put(TAG_SHUTTER, "Shutter");
_tagNameMap.put(TAG_FLASH_STATUS, "Flash Status");
_tagNameMap.put(TAG_SHUTTER_COUNT, "Shutter Count");
_tagNameMap.put(TAG_SONY_EXPOSURE_TIME, "Sony Exposure Time");
_tagNameMap.put(TAG_SONY_F_NUMBER, "Sony F Number");
_tagNameMap.put(TAG_RELEASE_MODE_2, "Release Mode 2");
_tagNameMap.put(TAG_INTERNAL_SERIAL_NUMBER, "Internal Serial Number");
_tagNameMap.put(TAG_LENS_MOUNT, "Lens Mount");
_tagNameMap.put(TAG_LENS_FORMAT, "Lens Format");
_tagNameMap.put(TAG_LENS_TYPE_2, "Lens Type 2");
_tagNameMap.put(TAG_DISTORTION_CORR_PARAMS_PRESENT, "Distortion Corr Params Present");
_tagNameMap.put(TAG_APS_C_SIZE_CAPTURE, "APS-C Size Capture");
_tagNameMap.put(TAG_LENS_SPEC_FEATURES, "Lens Spec Features");
_tagNameMap.put(TAG_SHUTTER_COUNT_3, "Shutter Count 3");
}

public SonyTag9050bDirectory()
{
this.setDescriptor(new SonyTag9050bDescriptor(this));
}

@Override
@NotNull
public String getName()
{
return "Sony 9050B";
}

@Override
@NotNull
protected HashMap<Integer, String> getTagNameMap()
{
return _tagNameMap;
}

public static SonyTag9050bDirectory read(byte[] bytes)
{
SonyTag9050bDirectory dir = new SonyTag9050bDirectory();

try {
// First, decipher the bytes
decipherInPlace(bytes);

ByteArrayReader reader = new ByteArrayReader(bytes);
reader.setMotorolaByteOrder(false);

// Shutter
int offset = TAG_SHUTTER;
int shutter0 = reader.getUInt16(offset);
int shutter1 = reader.getUInt16(offset + 2);
int shutter2 = reader.getUInt16(offset + 4);
dir.setIntArray(TAG_SHUTTER, new int[]{shutter0, shutter1, shutter2});

// FlashStatus
offset = TAG_FLASH_STATUS;
int flashStatus = reader.getUInt8(offset);
dir.setInt(TAG_FLASH_STATUS, flashStatus);

// ShutterCount
offset = TAG_SHUTTER_COUNT;
long shutterCount = reader.getUInt32(offset);
dir.setLong(TAG_SHUTTER_COUNT, shutterCount);

// SonyExposureTime
offset = TAG_SONY_EXPOSURE_TIME;
int expTime = reader.getUInt16(offset);
float expTimeFlt = (float)Math.pow(2, 16 - (expTime / 256));
DecimalFormat format = new DecimalFormat("0.#############");
format.setRoundingMode(RoundingMode.HALF_UP);
dir.setFloat(TAG_SONY_EXPOSURE_TIME, expTimeFlt);

// SonyFNumber
offset = TAG_SONY_F_NUMBER;
int fNumber = reader.getUInt16(offset);
dir.setInt(TAG_SONY_F_NUMBER, fNumber);

// ReleaseMode2
// ReleaseMode2

offset = TAG_INTERNAL_SERIAL_NUMBER;
int serialNum0 = reader.getUInt8(offset);
int serialNum1 = reader.getUInt8(offset + 1);
int serialNum2 = reader.getUInt8(offset + 2);
int serialNum3 = reader.getUInt8(offset + 3);
int serialNum4 = reader.getUInt8(offset + 4);
int serialNum5 = reader.getUInt8(offset + 5);
int[] serialNumber =
new int[]{serialNum0, serialNum1, serialNum2, serialNum3, serialNum4, serialNum5};
dir.setIntArray(TAG_INTERNAL_SERIAL_NUMBER, serialNumber);

// LensMount
offset = TAG_LENS_MOUNT;
int lensMount = reader.getUInt8(offset);
dir.setInt(TAG_LENS_MOUNT, lensMount);

// LensFormat
offset = TAG_LENS_FORMAT;
int lensFormat = reader.getUInt8(offset);
dir.setInt(TAG_LENS_FORMAT, lensFormat);

// LensType2
offset = TAG_LENS_TYPE_2;
int lensType2 = reader.getUInt16(offset);
dir.setInt(TAG_LENS_TYPE_2, lensType2);

// DistortionCorrParamsPresent
offset = TAG_DISTORTION_CORR_PARAMS_PRESENT;
int distortCorrParamsPresent = reader.getUInt8(offset);
dir.setInt(TAG_DISTORTION_CORR_PARAMS_PRESENT, distortCorrParamsPresent);

// APS-CSizeCapture
offset = TAG_APS_C_SIZE_CAPTURE;
int apsCSizeCapture = reader.getUInt8(offset);
dir.setInt(TAG_APS_C_SIZE_CAPTURE, apsCSizeCapture);

// LensSpecFeatures
offset = TAG_LENS_SPEC_FEATURES;
byte[] lensSpecFeatures = reader.getBytes(offset, 2);
dir.setByteArray(TAG_APS_C_SIZE_CAPTURE, lensSpecFeatures);

// ShutterCount3
// APS-CSizeCapture
// LensSpecFeatures

} catch (IOException e) {
dir.addError(e.getMessage());
}

return dir;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ public class SonyType1MakernoteDirectory extends Directory
public static final int TAG_CAMERA_INFO = 0x0010;
public static final int TAG_FOCUS_INFO = 0x0020;

// https://exiftool.org/TagNames/Sony.html#Tag9050a
public static final int TAG_9050A = 0x0030;
// https://exiftool.org/TagNames/Sony.html#Tag9050b
public static final int TAG_9050B = 0x9050;
// https://exiftool.org/TagNames/Sony.html#Tag9050c
public static final int TAG_9050C = 0x0040;

public static final int TAG_IMAGE_QUALITY = 0x0102;
public static final int TAG_FLASH_EXPOSURE_COMP = 0x0104;
public static final int TAG_TELECONVERTER = 0x0105;
Expand Down

0 comments on commit ae792e7

Please sign in to comment.