TL;DR
FitMesg.h
と FitMesg.mm
を修正してビルドした libFitSdkCppiOS.a
とヘッダーファイルを差し替える。
修正内容は記事の末尾。
不具合詳細
GARMIN FIT SDKはC言語/C++/Java/C#で提供されているが、同梱されているMacStaticLibを使用することでObjective-Cから使用することが出来、macOSやiOSのアプリから使用することができる。
しかし、このMacStaticLibにはバグがあり、メッセージのサイズが256Byte以上になるとヘッダーに書き込まれるデータ長とファイル末尾のCRCの値がおかしくなり、デコードできないFITファイルが生成されてしまう。
再現コード
FIT SDKに同梱されているサンプルプロジェクトにコードを追加したもの。
https://github.com/rseki-sonix/fit-ios-broken-header
device_infoメッセージのproduct_nameとdescriptorに長い文字列を追加するとデコード処理に失敗するようになる。
原因
Objective-CからC++ FIT SDKを使用するためのラッパークラス FitEncode
はヘッダに書き込むデータ長を書き込んだメッセージ長から積算している。
このメッセージ長の型が FIT_UINT8
(UINT8のtypedef)になっており、256Byte以上の長さだとオーバーフローしてしまう。
そのため、256Byte以上のメッセージが書き込まれるたびにヘッダーのデータ長が255Byte分短くなる。
また、ファイル末尾のCRCもそのデータ長をもとに計算されているためCRCもおかしくなってしまう。
生成済みのFITファイルを修復する
生成されたFITファイルはデータ長とCRCだけがおかしいため、適切なデータ長とCRCを書き込めばデコードすることができる。
ファイルサイズからヘッダ長(14Byte)と末尾CRCの長さ(2Byte)を引いた値を計算する。
ヘッダーの内容についてはFit Protocolを参照。
今回生成されたファイルは453Byteのためデータ長は
453 - 14 - 2 = 437 = 0x01B5
となる。
CRCは自分で計算してもよいが、FitCSVToolに-dオプションを追加すると計算した数値が表示されるためそれを用いる。
今回は 0x03A9
だった。
書き換えるとFitCSVToolでデコードできるようになる。
MacStaticLibを修正する
GARMIN Forumsでは"your own risk"でFitMesgを修正してもいいかもとのこと。
単純にメッセージサイズの型をUINT16に変更することで正しいデータ長とCRCを持つFITファイルを生成できる。
以下の二箇所の変更が必要。
#include "fit_mesg_definition.hpp"
@interface FitMesg : NSObject
// - (FIT_UINT8) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg;
// - (FIT_UINT8) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg withDef:(const fit::MesgDefinition *)mesgDef;
- (FIT_UINT16) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg;
- (FIT_UINT16) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg withDef:(const fit::MesgDefinition *)mesgDef;
@end
#endif /* FIT_MESG_H */
@implementation FitMesg
// - (FIT_UINT8) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg
- (FIT_UINT16) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg
{
return [self Write:file forMesg:mesg withDef:FIT_NULL];
}
// - (FIT_UINT8) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg withDef:(const fit::MesgDefinition *)mesgDef
- (FIT_UINT16) Write:(FILE *) file forMesg:(const fit::Mesg *)mesg withDef:(const fit::MesgDefinition *)mesgDef
{
fit::MesgDefinition mesgDefOnNull;
// FIT_UINT8 mesgSize = 1;
FIT_UINT16 mesgSize = 1;
FIT_UINT8 fieldSize = 0;
FIT_UINT8 byte;
修正した静的ライブラリは以下の方法で行った。