function readstruct = structread(fid, structdef);
% STRUCTREAD read in structured data
%   READSTRUCT = STRUCTREAD(FID, STRUCTDEF)
%
%   Read in structured data from file handle FID according to STRUCTDEF,
%   which is in the ordered format of structdef.fieldname = datatype
%   For example, structdef.isbn = '10*uint8' means that the ISBN field to be
%   read consists of 10 unsigned integers.
%

%   Peter Li 30-Aug-05
%   Some rights reserved.  Licensed under Creative Commons: http://creativecommons.org/licenses/by-nc-sa/3.0/

error(nargchk(2, 2, nargin));
if ~isstruct(structdef), error('Second argument must be a struct definition'); end

readstruct = struct([]);

% Get file length
pos = ftell(fid);
fseek(fid, 0,'eof');
flen = ftell(fid);
if fseek(fid, pos, 'bof') == -1, error(['Received error on file seek: ' ferror(fid)]); end

% Calculate how many bytes the file has left, compare to size of struct
fleft = flen - pos;
structsize = structsizeof(structdef);
if structsize > fleft, error(['Size of Struct (' num2str(structsize) ') > Bytes Left in File (' num2str(fleft) ')']); end

% Main section   
fields = fieldnames(structdef);
for i = 1:length(fields),
    field = fields{i};
    datatype  = structdef.(field);

    % Parse datatype
    starind = strfind(datatype, '*');
    if length(starind) > 1, error(['Incorrect datatype format: ' datatype]); end
    if starind,
        num = str2num(datatype(1:starind-1));
        datatype = datatype(starind + 1:end);
    else
        num = 1;
    end

    % Read value(s)
    [value, readnum] = fread(fid, num, datatype);
    if readnum ~= num, error(['Failed to read more than ' num2str(readnum) ' values for ' field '(' num2str(num) ')']); end

    % If value is character string, convert to char.
    if strfind(datatype, 'char'),
        value = char(value);
    end

    % Handle fieldnames ending in numbers, e.g. if fields UNKNOWN1, UNKNOWN2,
    % etc. exist, we will concatenate them all as a cell array into a single
    % UNKNOWN field.

    % See if filename ends in a number
    for i = fliplr(1:length(field)),
        if ~length(str2num(field(i:end))),
            break;
        end
    end
    if i < length(field),
        appendnum = str2num(field(i+1:end));
        appendfield = field(1:i);

        % See if appendfield exists and whether appendnum matches the number
        % of elements to be stored there.
        if ~isfield(readstruct, appendfield),
            if appendnum == 1, field = appendfield; end
        else
            if ~iscell(readstruct.(appendfield)),
                if appendnum == 2, field = appendfield; end
            else
                if appendnum == length(readstruct.(appendfield)) + 1, field = appendfield; end
            end
        end
    end

    readstruct(1).(field) = structappend(readstruct, field, value');
end
