入出力のイメージ
入力
sample.mot
S113000000000100000001000000010000000100E8
S113001000000100000001000000010000000100D8
S113002000000100000001000000010000000100C8
S11300D00000010000000100000001000000010018
S11300E00000010000000100000001000000010008
S10700F00000010007
出力(中身はただのバイナリ)
生成されるファイル
_00000000_0000002F.bin
_000000D0_000000F3.bin
入力motに対する前提条件
- アドレスが昇順になっていない場合の連結は未サポート(つながった領域でも細切れにbinが生成されるはず。)
- intの最大値の半分(1024MB相当)以上の大きさのデータは多分扱えないです。1
ソースコード
なぐり書きコードでろくにテストもしてないので、参考程度ということにしてください。
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
internal class DataBlocks
{
// Key: address
public SortedList<long, DataBlock> Blocks{get;private set;}
DataBlock _lastAdded;
public bool ConsistencyPassed{get;private set;}
public DataBlocks()
{
Blocks = new SortedList<long, DataBlock>();
_lastAdded = null;
ConsistencyPassed = false;
}
// エラー(重複検出時)は return false
public bool Append(DataBlock b)
{
if ( b.Size == 0 ) {
return true; // 長さがないデータは無視する(正常扱い)
}
if ( _lastAdded == null ) { // まだデータがない
Blocks.Add(b.Address, b);
_lastAdded = b;
return true;
}
ConsistencyPassed = false;
if ( Blocks.ContainsKey(b.Address) ) {
// overlap
return false;
}
if ( _lastAdded.TryAppendBlock(b) ) {
// 最終要素に連結できた。
// nothing to do.
return true;
}
else {
Blocks.Add(b.Address, b);
_lastAdded = b;
return true;
}
}
// overlapがないかチェック。
// (memo: 連結できれば連結するようにしたい TryToConcatAndCheckNoOverlap ... これは未実装)
public bool CheckNoOverlap()
{
DataBlock prevBlock = null;
foreach ( var b in Blocks.Values ) {
if ( prevBlock != null ){
if ( b.IsOverlap(prevBlock) ) {
return false;
}
//else if ( prevBlock.EndAddress+1 == b.Address ) { // 連結可能
// ただ、foreach内でこれをやると不整合が起きそう
// 未実装
//}
}
prevBlock = b;
}
ConsistencyPassed = true;
return true;
}
public static DataBlocks FromMotFile(string fileName)
{
var res = new DataBlocks();
string[] ss = File.ReadAllLines(fileName);
int lineNo = 0;
foreach ( string s in ss ) {
lineNo++;
if (String.IsNullOrWhiteSpace(s)) {
continue;
}
var oneRecord = OneRecord.FromString(s);
if ( oneRecord == null ) {
Console.WriteLine("Warning: Line " + lineNo.ToString() + " is ignored.");
}
else {
if ( !res.Append(DataBlock.FromOneRecord(oneRecord)) ) {
// conflicted(overlapp)
Console.WriteLine("Warning: Line " + lineNo.ToString() + " is ignored (start address conflicted).");
}
}
}
return res;
}
}
internal class DataBlock
{
public long Address{get;private set;}
public long EndAddress{get{return Address + Size - 1;}}
public long Size{get;private set;} // 有効データ部分のサイズ
long InternalSize{get{return Data.Length;}}
// Array.Resize が ref を使っており、実体をフィールド宣言せざるをえない。
// (プロパティはref/outで参照できない。)
byte[] _data;
public byte[] Data{get{return _data;}private set{_data=value;}} // バッファとして確保しているサイズを含む
public DataBlock(long addr, byte[] data)
{
Address = addr;
Size = data.Length;
Data = new byte[data.Length];
Array.Copy(data,0,Data,0,Size);
}
public static DataBlock FromOneRecord(OneRecord record)
{
return new DataBlock(record.Address, record.DataBytes);
}
// 有効データが入っている部分のデータを返す
public byte[] GetActualDataBytes()
{
if ( Size == InternalSize ) {
return Data;
}
else {
byte[] t = new byte[Size];
Array.Copy(Data, 0, t, 0, Size);
return t;
}
}
public void SaveAsBinaryFile(string destPath)
{
File.WriteAllBytes(destPath, GetActualDataBytes());
}
// アドレスが後ろ側につながっていれば連結する。
// 結合できる(=できた)場合は true を返す。
public bool TryAppendBlock(DataBlock b)
{
if (EndAddress+1 == b.Address) {
if ( InternalSize < Size + b.Size ) { // サイズが足りない。
int newInternalSize;
checked {
// リサイズする
newInternalSize = (int)(Size + b.Size);
if ( newInternalSize < (int)(2*Size) ) {
// リサイズが必要となる頻度を抑制するため、大きめに確保しておく
newInternalSize = (int)(2*Size);
}
}
Array.Resize(ref _data, newInternalSize);
}
Array.Copy(b.Data, 0, Data, Size, b.Size); // bの有効データを、thisの有効データの末尾(位置=Size)以降にコピーする
Size += b.Size;
return true;
}
return false;
}
public bool IsOverlap(DataBlock b)
{
return !IsExclusive(b);
}
bool IsExclusive(DataBlock b)
{
if ( EndAddress < b.Address ){ return true; }
if ( b.EndAddress < Address ){ return true; }
return false;
}
}
// S-Record 1行分のデータを扱う
class OneRecord
{
// This program ignores S5, S7, S8, S9.
static Regex rSn = new Regex(@"^S([01235789])");
static Regex rS0 = new Regex(@"^S(0)([0-9A-Fa-f]{2})((?:[0-9A-Fa-f]{2})*)([0-9A-Fa-f]{2})$");
static Regex rS1 = new Regex(@"^S(1)([0-9A-Fa-f]{2})([0-9A-Fa-f]{4})((?:[0-9A-Fa-f]{2})+)([0-9A-Fa-f]{2})$");
static Regex rS2 = new Regex(@"^S(2)([0-9A-Fa-f]{2})([0-9A-Fa-f]{6})((?:[0-9A-Fa-f]{2})+)([0-9A-Fa-f]{2})$");
static Regex rS3 = new Regex(@"^S(3)([0-9A-Fa-f]{2})([0-9A-Fa-f]{8})((?:[0-9A-Fa-f]{2})+)([0-9A-Fa-f]{2})$");
public int RecordType{get;private set;}
public int RecordLength{get;private set;}
public long Address{get;private set;}
public byte[] DataBytes{get;private set;}
public byte CheckSum{get;private set;}
public int DataLength{get{return DataBytes.Length;}}
public long EndAddress{get{return Address + DataBytes.Length -1;}}
public bool IsValidSum{get{return CalcCheckSum()==CheckSum;}}
byte CalcCheckSum()
{
int sum=0;
sum += RecordLength;
sum += (byte)(Address>>24);
sum += (byte)(Address>>16);
sum += (byte)(Address>> 8);
sum += (byte) Address;
foreach(byte b in DataBytes) {
sum += b;
}
return (byte)(~sum);
}
OneRecord()
{
}
public static OneRecord FromString(string s, bool checkFormat = true)
{
Match m;
m = rSn.Match(s);
if ( !m.Success ) {
return null;
}
m = rS1.Match(s);
if ( !m.Success ) { m = rS2.Match(s); }
if ( !m.Success ) { m = rS3.Match(s); }
if ( !m.Success ) { return null; }
var ret = new OneRecord();
ret.RecordType = Convert.ToInt32(m.Groups[1].Value);
ret.RecordLength = Convert.ToInt32(m.Groups[2].Value, 16);
ret.Address = Convert.ToInt64(m.Groups[3].Value, 16);
ret.DataBytes = HexStrToByteArray(m.Groups[4].Value);
ret.CheckSum = Convert.ToByte(m.Groups[5].Value, 16);
if ( checkFormat ) {
if ( ret.RecordLength*2 != s.Length - 4) {
Console.WriteLine("LengthError");
return null;
}
if ( !ret.IsValidSum ){
Console.WriteLine("ChkSumError");
return null;
}
}
return ret;
}
static int HexCharToInt(char c)
{
if('0'<=c&&c<='9'){return c-'0';}
if('A'<=c&&c<='F'){return (c-'A')+10;}
if('a'<=c&&c<='f'){return (c-'a')+10;}
return -1;
}
static byte[] HexStrToByteArray(string s)
{
// length of the argument `s` must be even value.
byte[] a = new byte[s.Length/2];
for (int pos=0 ; pos+1<s.Length ; pos+=2) {
a[pos/2] = (byte)( (HexCharToInt(s[pos])<<4) | HexCharToInt(s[pos+1]) );
}
return a;
}
}
class MotFileTest
{
[STAThread]
static void Main(string[] args)
{
if ( args.Length != 1 ) { return; }
var dataBlocks = DataBlocks.FromMotFile(args[0]);
if ( dataBlocks.CheckNoOverlap() ) {
foreach( var block in dataBlocks.Blocks.Values ) {
string fileName = Path.GetFullPath("_"+block.Address.ToString("X08")+"_"+block.EndAddress.ToString("X08")+".bin");
Console.WriteLine(fileName);
block.SaveAsBinaryFile(fileName);
}
}
}
}
ひとりごと
アドレスでソートしておいたほうがデータを扱いやすいかなと思い、SortedList
を初めて使ってみた。
List
よりDictionary
に近い感じ(?)
参考サイト
-
おそらく下記コードの
TryAppendBlock
あたりの処理でオーバーフローするはず。 ↩