Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions SpssLib/Compression/DecompressedDataStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Text;
using System.IO;

using SpssLib.FileParser;

namespace SpssLib.Compression
{
class DecompressedDataStream: Stream
Expand All @@ -24,15 +26,15 @@ class DecompressedDataStream: Stream
private byte[] _systemMissingBytes;
private byte[] _spacesBytes;

private BinaryReader _reader;
private DualBinaryReader _reader;

public DecompressedDataStream(Stream compressedDataStream, double bias, double systemMissing)
public DecompressedDataStream(Stream compressedDataStream, double bias, double systemMissing, bool isLittleEndian)
{
CompressedDataStream = compressedDataStream;
Bias = bias;
SystemMissing = systemMissing;
_reader = new BinaryReader(compressedDataStream, Encoding.ASCII);

_reader = new DualBinaryReader(compressedDataStream, Encoding.ASCII);
_reader.IsLittleEndian = isLittleEndian;
_spacesBytes = Encoding.ASCII.GetBytes(SpaceString);
_systemMissingBytes = BitConverter.GetBytes(SystemMissing);
}
Expand Down Expand Up @@ -163,11 +165,19 @@ private bool ParseNextInstructionSet()
{
}

else if (instruction > 0 && instruction < 252) // compressed value
else if (instruction > 0 && instruction < 252) // compressed value (small 1-byte integers)
{
// compute actual value:
double value = instruction - Bias;
_elementBuffer[bufferPosition++] = BitConverter.GetBytes(value);
byte[] element = BitConverter.GetBytes(value);
if(_reader.IsLittleEndian)
_elementBuffer[bufferPosition++] = BitConverter.GetBytes(value);
else
{
byte[] revElement = new byte[InstructionSetByteSize];
Helper.ReverseArray(element, revElement);
_elementBuffer[bufferPosition++] = revElement;
}
}
else if (instruction == 252) // end of file
{
Expand Down
2 changes: 1 addition & 1 deletion SpssLib/DataReader/SpssReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class SpssReader : IDisposable
// TODO add needed metadata info (use SpssOptions?)

/// <summary>
/// A collection of variables read from teh file
/// A collection of variables read from the file
/// </summary>
public ICollection<Variable> Variables { get; private set; }
/// <summary>
Expand Down
113 changes: 113 additions & 0 deletions SpssLib/FileParser/DualBinaryReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.IO;
using System.Text;

namespace SpssLib.FileParser
{
public static class Helper
{
public static void ReverseArray(byte[] source, byte[] target)
{
int len = source.Length;
for (int i = 0; i < len; i++)
target[i] = source[len - 1 - i];
}
}

// Binary reader that supports both little-endian and big-endian byte orders. It assumes that the current environment is little-indian.
public class DualBinaryReader : BinaryReader
{
public DualBinaryReader(Stream stream, Encoding encoding) : base(stream, encoding)
{

}

public bool IsLittleEndian { get; set; } = true;

public override Int16 ReadInt16()
{
Int16 i;

if (this.IsLittleEndian)
i = base.ReadInt16();
else
{
byte b1 = this.ReadByte();
byte b0 = this.ReadByte();
i = (short)(b1 << 8 + b0);
}

return i;
}

public override UInt16 ReadUInt16()
{
UInt16 i;

if (this.IsLittleEndian)
i = base.ReadUInt16();
else
{
byte b1 = this.ReadByte();
byte b0 = this.ReadByte();
i = (UInt16)(b1 << 8 + b0);
}

return i;
}

public override int ReadInt32()
{
int i;

if (this.IsLittleEndian)
i = base.ReadInt32();
else
{
i = this.ReadByte();

for (int k = 0; k < 3; k++)
i = (i << 8) | this.ReadByte();
}

return i;
}

public override uint ReadUInt32()
{
uint i;

if (this.IsLittleEndian)
i = base.ReadUInt32();
else
{
i = this.ReadByte();

for (int k = 0; k < 3; k++)
i = (i << 8) | this.ReadByte();
}

return i;
}

public override double ReadDouble()
{
double f;

if (this.IsLittleEndian)
f = base.ReadDouble();
else
{
UInt64 l = this.ReadByte();

for(int k = 0; k < 7; k++)
l = (l << 8) | this.ReadByte();

f = BitConverter.ToDouble( BitConverter.GetBytes(l), 0);
}

return f;
}

}
}
13 changes: 13 additions & 0 deletions SpssLib/FileParser/MetaData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ internal set

public IList<BaseInfoRecord> InfoRecords { get; private set; }

public bool IsLittleEndian
{
get
{
if (this.MachineIntegerInfo.Endianness == 2)
return true;
else if (this.MachineIntegerInfo.Endianness == 1)
return false;
else
throw new SpssFileFormatException("Failed to detect endianness.");
}
}

public double SystemMissingValue { get; private set; }
// Count number of variables (the number of variable-records with a name,
// the rest is part of a long string variable), this includes the variables
Expand Down
6 changes: 3 additions & 3 deletions SpssLib/FileParser/Records/BaseInfoRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void WriteRecord(BinaryWriter writer)
WriteInfo(writer);
}

public void FillRecord(BinaryReader reader)
public void FillRecord(DualBinaryReader reader)
{
ItemSize = reader.ReadInt32();
ItemCount = reader.ReadInt32();
Expand Down Expand Up @@ -49,7 +49,7 @@ protected void CheckInfoHeader(int itemSize = -1, int itemCount = -1)
}

protected abstract void WriteInfo(BinaryWriter writer);
protected abstract void FillInfo(BinaryReader reader);
protected abstract void FillInfo(DualBinaryReader reader);
}

public class UnknownInfoRecord : BaseInfoRecord
Expand Down Expand Up @@ -78,7 +78,7 @@ protected override void WriteInfo(BinaryWriter writer)
writer.Write(Data);
}

protected override void FillInfo(BinaryReader reader)
protected override void FillInfo(DualBinaryReader reader)
{
Data = reader.ReadBytes(ItemCount * ItemSize);
}
Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/CharacterEncodingRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected override void WriteInfo(BinaryWriter writer)
writer.Write(bytes);
}

protected override void FillInfo(BinaryReader reader)
protected override void FillInfo(DualBinaryReader reader)
{
CheckInfoHeader(1); // items must be of size 1 (byte)

Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/DictionaryTerminationRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void WriteRecord(BinaryWriter writer)
writer.Write(0);
}

public void FillRecord(BinaryReader reader)
public void FillRecord(DualBinaryReader reader)
{
// skip filler
reader.ReadInt32();
Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/DocumentRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public DocumentRecord(IList<string> lines)
LineCount = lines.Count;
}

public void FillRecord(BinaryReader reader)
public void FillRecord(DualBinaryReader reader)
{
LineCount = reader.ReadInt32();
LineCollection = new List<string>();
Expand Down
4 changes: 2 additions & 2 deletions SpssLib/FileParser/Records/EbcdicHeaderRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace SpssLib.FileParser.Records
{
internal class EbcdicHeaderRecord : IRecord
{
public NotSupportedException Exception => new NotSupportedException("EBCDIC???? Who uses that? Honestly!!");
public NotSupportedException Exception => new NotSupportedException("EBCDIC records not supported.");

public RecordType RecordType => RecordType.EbcdicHeaderRecord;

Expand All @@ -14,7 +14,7 @@ public void WriteRecord(BinaryWriter writer)
throw Exception;
}

public void FillRecord(BinaryReader reader)
public void FillRecord(DualBinaryReader reader)
{
throw Exception;
}
Expand Down
16 changes: 15 additions & 1 deletion SpssLib/FileParser/Records/HeaderRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,24 @@ public void WriteRecord(BinaryWriter writer)
writer.Write(new byte[3]);
}

public void FillRecord(BinaryReader reader)
public void FillRecord(DualBinaryReader reader)
{
ProductName = new string(reader.ReadChars(60));
LayoutCode = reader.ReadInt32();
switch(LayoutCode)
{
case 2:
case 3:
reader.IsLittleEndian = true;
break;
case 0x02000000:
case 0x03000000:
reader.IsLittleEndian = false;
break;
default:
throw new SpssFileFormatException("Failed to detect endianness.");
}

NominalCaseSize = reader.ReadInt32();
Compressed = reader.ReadInt32() == 1;
WeightIndex = reader.ReadInt32();
Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/IRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal interface IRecord
{
RecordType RecordType { get; }
void WriteRecord(BinaryWriter writer); // TODO: split to internal interface
void FillRecord(BinaryReader reader);
void FillRecord(DualBinaryReader reader);
void RegisterMetadata(MetaData metaData);
}

Expand Down
4 changes: 2 additions & 2 deletions SpssLib/FileParser/Records/IRecordParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace SpssLib.FileParser.Records
internal interface IRecordParser
{
RecordType Accepts { get; }
IRecord ParseRecord(BinaryReader reader);
IRecord ParseRecord(DualBinaryReader reader);
}

internal class GeneralRecordParser<TRecord> : IRecordParser where TRecord : IRecord
Expand All @@ -18,7 +18,7 @@ public GeneralRecordParser(RecordType accepts)
Accepts = accepts;
}

public IRecord ParseRecord(BinaryReader reader)
public IRecord ParseRecord(DualBinaryReader reader)
{
TRecord record = CreateRecord();
record.FillRecord(reader);
Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/InfoRecordParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public InfoRecordParser(IDictionary<int, Type> infoRecordsTypes)
_infoRecordsTypes = infoRecordsTypes;
}

public IRecord ParseRecord(BinaryReader reader)
public IRecord ParseRecord(DualBinaryReader reader)
{
IRecord record = CreateRecord(reader);
record.FillRecord(reader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected override void WriteInfo(BinaryWriter writer)
writer.Write(MissingLowestValue);
}

protected override void FillInfo(BinaryReader reader)
protected override void FillInfo(DualBinaryReader reader)
{
CheckInfoHeader(8, 3);

Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/MachineIntegerInfoRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ protected override void WriteInfo(BinaryWriter writer)
writer.Write(CharacterCode);
}

protected override void FillInfo(BinaryReader reader)
protected override void FillInfo(DualBinaryReader reader)
{
// Must have 8 int of 32 bits
CheckInfoHeader(itemSize:4, itemCount:8);
Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/ValueLabelRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void WriteRecord(BinaryWriter writer)
}
}

public void FillRecord(BinaryReader reader)
public void FillRecord(DualBinaryReader reader)
{
LabelCount = reader.ReadInt32();
_labelsRaw = new Dictionary<byte[], KeyValuePair<byte, byte[]>>();
Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/VariableDataInfoRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ protected void BuildDataArray()
Data = buffer;
}

protected override void FillInfo(BinaryReader reader)
protected override void FillInfo(DualBinaryReader reader)
{
CheckInfoHeader(1);
Data = reader.ReadBytes(ItemCount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ protected override void WriteInfo(BinaryWriter writer)
}
}

protected override void FillInfo(BinaryReader reader)
protected override void FillInfo(DualBinaryReader reader)
{
CheckInfoHeader(4);

Expand Down
2 changes: 1 addition & 1 deletion SpssLib/FileParser/Records/VariableRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ public void WriteRecord(BinaryWriter writer)

}

public void FillRecord(BinaryReader reader)
public void FillRecord(DualBinaryReader reader)
{
Type = reader.ReadInt32();
HasVariableLabel = reader.ReadInt32() == 1;
Expand Down
Loading