function [msg,catalog,ws,versions] = readsb(pathname,filename)
%READASB Read an ASCII format SystemBuild model file.
%
%    READSB(PATH,NAME) opens a file NAME in the directory PATH and reads it.
%    File content is interpreted as the SystemBuild format file.
%
%  See also SB2SL.

% A. S. Bozin, 5-3-97.
% Copyright 1997-2000 The MathWorks, Inc.
% $Revision: 1.12 $ $Date: 2000/09/01 14:07:37 $

   %
   % Open the filename and read its contents
   %
   [a,msg] = fgetasc(cat(2,pathname,filename));
   if (isempty(msg))
      sbhndlmsgs('write',sprintf('File %s successfully loaded.',filename));
      %
      % Browse through the file contents and extract useful information
      %
      aLen = size(a,2);
      bias = 1;					   % Initial offset
      %
      % Get the variables
      %
      ws = [];					   % Workspace
      LF = char(10);
      SP = char(32);
      indx = mfindstr(a,bias,'xmath',33);	   % Limit the search
      if (indx ~= 0)
	 %
	 % Check whether ascii or binary save function used
	 %
	 inda = mfindstr(a,bias,'A',indx);
	 if (inda == 0)
	    msg = {'File not saved with ASCII_SAVE option.'};
	 else
	    %
	    % Find out on which architeture this file has been created
	    %
	    aRange = 1:4;
	    if (strcmp(a(inda+aRange),'  RR'))
	       arch = 0;
	    else
	       arch = 1;
	    end
	    bias = mfindstr(a,indx,LF);
	    %
	    % Read the data segment
	    %
	    sbhndlmsgs('write','Getting variables ...','append');
	    mdtr = 1;				   % More data to read
	    catchCmd = 'mdtr=0;';
	    while (mdtr)
	       [typ,bias] = mgetnint(a,bias,1);
	       switch (typ)
	       case 0			   	   % End of data segment
		  mdtr = 0;
	       case 1			   	   % Data
		  [nChr,bias] = mgetnint(a,bias,1);
		  [wsName,bias] = mgetnstr(a,bias,nChr);
		  [nChr,bias] = mgetnint(a,bias,1);
		  [matName,bias] = mgetnstr(a,bias,nChr);
		  wsField = cat(2,'ws.',wsName);
		  [nChr,bias] = mgetnint(a,bias,1);
		  [hstr,bias] = mgetnrec(a,bias,nChr,arch);
		  rec = mgetnint(hstr,1,1);
		  [nChr,bias] = mgetnint(a,bias,1);
		  [record,bias] = mgetnrec(a,bias,nChr,arch);
		  indx = mfindstr(record,1,LF);
		  switch (rec)
		  case 2			   % A string  matrix
		     [hint,indx] = mgetnint(record,indx,3);
		     dims = hint([2 3]);
		     nnum = prod(dims);
		     if (nnum == 1)
			[nChr,indx] = mgetnint(record,indx,1);
			xmat = mgetnstr(record,indx,nChr);
		     else
			xmat = cell(dims);
			for (k = 1:dims(1))
			   for (l = 1:dims(2))
			      [nChr,indx] = mgetnint(record,indx,1);
			      [xmat{k,l},indx] = mgetnstr(record,indx,nChr);
			   end
			end
		     end
		  case 3			   % A matrix
		     [hint,indx] = mgetnint(record,indx,4);
		     dims = hint([3 2]);	   % Transposed matrix
		     xmat = zeros(dims);
		     nnum = prod(dims);
		     if (hint(4) == 1)
			[rmat,indx] = mgetnflt(record,indx,nnum);
			xmat(:) = rmat+sqrt(-1)*mgetnflt(record,indx,nnum);
		     else
			xmat(:) = mgetnflt(record,indx,nnum);
		     end
		     xmat = xmat.';
		  case 11			   % A log-space/lin-space vector
		     [hint,indx] = mgetnint(record,indx,2);
		     if (hint(2) == 1)
			[hflt,indx] = mgetnflt(record,indx,3);
			xmat = logspace(hflt(1),hflt(2),mgetnint(record,indx,1));
		     else
			[hflt,indx] = mgetnflt(record,indx,3);
			xmat = [hflt(1):hflt(3):hflt(2)];
		     end
		  case 15			   % PDM
		     [hint,indx] = mgetnint(record,indx,2);
		     switch (hint(2))
		     case 3			   % Domain vector
			[hint,indx] = mgetnint(record,indx,3);
			dims = hint([1 2]);
			domain = zeros(dims);
			nnum = prod(dims);
			if (hint(3) == 1)
			   [rmat,indx] = mgetnflt(record,indx,nnum);
			   [imat,indx] = mgetnflt(record,indx,nnum);
			   domain(:) = rmat+sqrt(-1)*imat;
			else
			   [domain(:),indx] = mgetnflt(record,indx,nnum);
			end
		     case 11			   % Default domain vector
			[hint,indx] = mgetnint(record,indx,1);
			if (hint == 0)
			   [hflt,indx] = mgetnflt(record,indx,4);
			   domain = [hflt(1):hflt(3):hflt(2)];
			end
		     otherwise
		     end
		     [hint,indx] = mgetnint(record,indx,1);
		     if (hint == 2)		   % Domain name
			[hint,indx] = mgetnint(record,indx,3);
			[domainName,indx] = mgetnstr(record,indx,hint(3));
		     else
			domainName = '';
		     end
		     [hint,indx] = mgetnint(record,indx,1);
		     if (hint == 2)		   % Column names
			[dims,indx] = mgetnint(record,indx,2);
			columnNames = cell(dims);
			nnum = prod(dims);
			for (k = 1:nnum)
			   [nChr,indx] = mgetnint(record,indx,1);
			   [columnNames{k},indx] = mgetnstr(record,indx,nChr);
			end
		     else
			columnNames = '';
		     end
		     [hint,indx] = mgetnint(record,indx,1);
		     if (hint == 2)		   % Row names
			[dims,indx] = mgetnint(record,indx,2);
			rowNames = cell(dims);
			nnum = prod(dims);
			for (k = 1:nnum)
			   [nChr,indx] = mgetnint(record,indx,1);
			   [rowNames{k},indx] = mgetnstr(record,indx,nChr);
			end
		     else
			rowNames = '';
		     end
		     [hint,indx] = mgetnint(record,indx,6);
		     pdm = zeros(hint(2),hint(1),length(domain));
		     dims = hint([5 4]);	   % Transposed matrix
		     mmat = zeros(dims);
		     nnum = prod(dims);
		     if (hint(6) == 1)
			[rmat,indx] = mgetnflt(record,indx,nnum);
			mmat(:) = rmat+sqrt(-1)*mgetnflt(record,indx,nnum);
		     else
			mmat(:) = mgetnflt(record,indx,nnum);
		     end		     
		     pdm(:) = mmat;
		     pdm = permute(pdm,[2 1 3]);
		     xmat = struct('pdm',pdm,...
				   'domain',domain,...
				   'domainName',domainName,...
				   'rowNames','',...
				   'columnNames','');
		     xmat.rowNames = rowNames;
		     xmat.columnNames = columnNames;
		  case 23			   % Toeplitz matrix
		     [hint,indx] = mgetnint(record,indx,5);
		     [xrow,indx] = mgetnflt(record,indx,prod(hint([3 4])));
		     [hint,indx] = mgetnint(record,indx,4);
		     [xcol,indx] = mgetnflt(record,indx,prod(hint([2 3])));
		     xcol(1) = xrow(1);
		     xmat = toeplitz(xcol,xrow);
		  otherwise		   	   % Ignore all other data types
		     xmat = [];
		  end
		  tryCmd = sprintf('%s=setfield(%s,''%s'',xmat);',wsField,wsField,matName);
		  eval(tryCmd,catchCmd);
	       case 2			   	   % Partition
		  [nChr,bias] = mgetnint(a,bias,1);
		  [wsName,bias] = mgetnstr(a,bias,nChr);
		  if (~isfield(ws,wsName))
		     ws = setfield(ws,wsName,[]);
		  end
		  bias = mfindstr(a,bias,LF);
	       otherwise			   % Ignore the rest of data segment
		  msg = {'Unknown data record encountered.'};
		  mdtr = 0;
	       end
	    end
	    sbhndlmsgs('write',' Done.','newline');
	    if (isempty(ws))
	       sbhndlmsgs('write','No variables found.');
	    else
	       sbhndlmsgs('write','Exporting variables to the base workspace ...','append');
	       wsName = fieldnames(ws);
	       nws = size(wsName,1);
	       for (cws = 1:nws)
		  partition = wsName{cws};
		  assignin('base',partition,getfield(ws,partition));
	       end
	       sbhndlmsgs('write',' Done.','newline');
	    end
	 end
      end
      if (isempty(msg))
	 %
	 % Get the MATRIXx version
	 %
	 matrixX = 'MATRIXx VERSION';
	 indx = mfindstr(a,bias,matrixX);
	 if (indx == 0)
	    msg = {'Could not get MATRIXx version number.'};
	 else
	    bias = indx+size(matrixX,2);  	   % Update offset
	    [mxVer,bias] = mgetnint(a,bias,1);
	    sbhndlmsgs('write',sprintf('MATRIXx Ver. %.2f',mxVer/100));
	    %
	    % Look for the SB version
	    %
	    systemBuild = 'SYSTEM BUILD';
	    indx = mfindstr(a,bias,systemBuild);
	    if (indx == 0)
	       systemBuild = 'SYS BUILD';
	       indx = mfindstr(a,bias,systemBuild);
	    end
	    if (indx == 0)
	       msg = {'Could not find SystemBuild model description.'};
	    else
	       bias = indx+size(systemBuild,2);	   % Update offset
	       [sbVer,bias] = mgetnint(a,bias,1);
	       sbhndlmsgs('write',sprintf('SystemBuild Ver. %.2f',sbVer/100));
	       if ((sbVer < 400) | (sbVer > 700))
		  msg = {'Unsupported version.';
			 'Loaded model saved using unsupported SystemBuild';
			 'version.'};
	       else
		  %
		  % Get the catalog entries
		  %
		  if (sbVer >= 600)
		     %
		     % Browse the catalog hierarchy
		     %
		     encrypted = 'ENC';
		     indx = mfindstr(a,bias,encrypted,bias+9);
		     if (indx ~= 0)
			msg = {'File cannot be opened as it contains an';
			       'encrypted component.'};
		     else
			bias = mfindstr(a,bias,LF);
			[catNum,bias] = mgetnint(a,bias,1);
			default = num2cell(zeros(catNum,1));
			catalog = struct('name','',...
					 'description','',...
					 'level',default,...
					 'depth',default,...
					 'contents',default,...
					 'code',default);
			for (cc = 1:catNum)
			   [indx,bias] = mgetnint(a,bias,1);
			   catalog(cc).level = indx;
			   cend = mfindstr(a,bias,SP);
			   catalog(cc).name = a(bias:cend-1);
			   bias = cend;
			end
		     end
		  else
		     %
		     % There is only the main catalog
		     %
		     catNum = 1;
		     catalog = struct('name','Main',...
				      'description','',...
				      'level',1,...
				      'depth',0,...
				      'contents',0,...
				      'code',0);
		     header = cat(2,'Main 18 4 0 -1',LF,'Main|Main catalog|',LF,'18 4 0 0',LF);
		     nChr = size(header,2);
		     if (nChr < bias)
			a(bias-nChr:bias-1) = header;
			bias = bias-nChr;
		     else
			a = cat(2,header,a(bias:end));
			bias = 1;
		     end
		  end
		  if (isempty(msg))
		     sbhndlmsgs('write','Parsing model description ...','append');
		     %
		     % Loop through the catalog hierarchy
		     %
		     if (sbVer >= 610)
			newver = logical(1);
		     else
			newver = logical(0);
		     end
		     for (cc = 1:catNum)
			%
			% Grab the header
			%
			bias = mfindstr(a,bias,catalog(cc).name);
			bias = bias+size(catalog(cc).name,2);
			[catChar,bias] = mgetnint(a,bias,1);
			[catInt,bias] = mgetnint(a,bias,1);
			[catFlt,bias] = mgetnint(a,bias,1);
			[catEnd,bias] = mgetnint(a,bias,1);
			[catHdr,bias] = mgetnstr(a,bias,catChar);
			[catChk,bias] = mgetnint(a,bias,catInt);
			%
			% Total number of super blocks, chars, ints and flts
			%
			[sbNum,bias] = mgetnint(a,bias,1);
			[sbChar,bias] = mgetnint(a,bias,1);
			[sbInt,bias] = mgetnint(a,bias,1);
			[sbFlt,bias] = mgetnint(a,bias,1);
			%
			% Read the contents
			%
			default = num2cell(zeros(sbNum+1,1));
			contents = struct('name','',...
					  'type',default,...
					  'signal',default,...
					  'sampleTime',default,...
					  'nBlocks',default,...
					  'nInputs',default,...
					  'nOutputs',default,...
					  'date',default,...
					  'time',default,...
					  'boundingBox',default,...
					  'partition','',...
					  'caller',default,...
					  'parent',[],...
					  'sbBlock',[],...
					  'sbChild',[],...
					  'sibling',[],...
					  'bkName',[],...
					  'bkBlock',[],...
					  'bkChild',[]);
			default = num2cell(zeros(sbNum,1));
			code = struct('str',[],...
				      'int',default,...
				      'flt',[]);
			names = cell(sbNum,1);
			%
			% For each superblock build a header
			%
			for (cb = 1:sbNum)
			   [name,bias] = mgetnstr(a,bias,32);
			   name = deblank(name);
			   names{cb} = cat(2,name,'|');
			   contents(cb).name = name;
			   [sbType,bias] = mgetnint(a,bias,1);
			   contents(cb).type = sbType;
			   [nBlocks,bias] = mgetnint(a,bias,1);
			   contents(cb).nBlocks = nBlocks;
			   [nInputs,bias] = mgetnint(a,bias,1);
			   contents(cb).nInputs = nInputs;
			   [nOutputs,bias] = mgetnint(a,bias,1);
			   contents(cb).nOutputs = nOutputs;
			   [date,bias] = mgetnint(a,bias,1);
			   contents(cb).date = date;
			   [time,bias] = mgetnint(a,bias,1);
			   contents(cb).time = time;
			   %
			   % Set the samping time
			   %
			   if (bitand(sbType,1) ~= 0)
			      %
			      % Not a continuous type superblock
			      %
			      [sampleTime,bias] = mgetnflt(a,bias,1);
			      [offsetTime,bias] = mgetnflt(a,bias,1);
			      switch (bitand(sbType,8191))
			      case 1		   % Discrete time superblock
				 contents(cb).sampleTime = [sampleTime offsetTime];
			      case 33		   % State Transition Diagram
				 contents(cb).sampleTime = [-1 0];
			      case 73		   % Data Store
				 contents(cb).sampleTime = [-1 0];
			      case 129		   % Triggered superblock
				 contents(cb).sampleTime = [-1 0];
			      otherwise	   	   % Procedure
				 contents(cb).sampleTime = [-1 0];
			      end
			   end
			end
			%
			% Loop through all the superblocks and extract block information
			%
			root = ones(1,sbNum);
			parent = cell(size(contents));
			for (cb = 1:sbNum)
			   [name,bias] = mgetnstr(a,bias,32);
			   name = deblank(name);
			   [nChr,bias] = mgetnint(a,bias,1);
			   [nInt,bias] = mgetnint(a,bias,1);
			   [nFlt,bias] = mgetnint(a,bias,1);
			   [last,bias] = mgetnint(a,bias,1);
			   [cstr,bias] = mgetnstr(a,bias,nChr);
			   [cint,bias] = mgetnint(a,bias,nInt);
			   [cflt,bias] = mgetnflt(a,bias,nFlt);
			   if (bitand(contents(cb).type,49152))
			   elseif (bitand(contents(cb).type,8192))
			   elseif (bitand(contents(cb).type,16))
			   else
			      [bstr,bint,bflt,bb] = mblksep(contents(cb).nBlocks,cstr,cint,cflt,newver);
			      code(cb).str = bstr;
			      code(cb).int = bint;
			      code(cb).flt = bflt;
			      contents(cb).boundingBox = bb;
			      %
			      % Set enable/trigger signal info as well as the output posting
			      %
			      ind = bint.sb{1}(15)+3;
			      switch (contents(cb).type)
			      case 1		   % Discrete time superblock
				 contents(cb).signal = bint.sb{1}(ind);
			      case 129		   % Triggered superblock
				 contents(cb).signal = bint.sb{1}(ind+[0 2]);
			      otherwise		   % All other types
				 contents(cb).signal = 0;
			      end
			      %
			      % Get the kids and parents
			      %
			      if (contents(cb).type ~= 73)
				 %
				 % Not a Data Store
				 %
				 [ind,pos,sibling] = mstrmatch(lower(names),lower(bstr.blk),bint.blk);
				 sbind = find(pos > 0);
				 if (~isempty(sbind))
				    [contents(cb).sbBlock,i] = sort(pos(sbind));
				    sbchild = ind(sbind(i));
				    contents(cb).sbChild = sbchild;
				    parent(sbchild) = cellcat(parent(sbchild),cb);
				 end
				 sbind = find(pos < 0);
				 if (~isempty(sbind))
				    [block,i] = sort(-pos(sbind));
				    lb = [1 find(diff(block))+1];
				    ub = [lb(2:end)-1 size(block,2)];
				    kids = sibling(sbind(i));
				    contents(cb).sibling = kids(lb);
				    bkblock = block(lb);
				    contents(cb).bkBlock = bkblock;
				    %
				    % Get the block name and the associated superblock indices
				    %
				    nblk = size(lb,2);
				    bkind = cell(1,nblk);
				    bkname = cell(1,nblk);
				    kids = ind(sbind(i));
				    for (k = 1:nblk)
				       bkind{k} = kids(lb(k):ub(k));
				       parent(bkind{k}) = cellcat(parent(bkind{k}),cb);
				       blkstr = bstr.blk{bkblock(k)};
				       nmind = mfindstr(blkstr,1,'|');
				       bkname{k} = blkstr(1:nmind-1);
				    end
				    contents(cb).bkName = bkname;
				    contents(cb).bkChild = bkind;
				 end
				 root(ind) = 0;
			      end
			   end
			end
			% Set all parents
			[contents.parent] = deal(parent{:});
			%
			% Initialize the root
			%
			ind = find(root);	   % Find the top level superblocks
			[contents(ind).parent] = deal(sbNum+1);
			[contents(ind).caller] = deal(sbNum+1);
			[contents(ind).partition] = deal('main');
			contents(end).type = -1;
			contents(end).nBlocks = sbNum;
			contents(end).partition = 'main';
			contents(end).sbChild = ind;
			dotPos = mfindstr(filename,1,'.',size(filename,2));
			%
			% Model name from the filename
			%
			if (dotPos)
			   contents(end).name = filename(1:dotPos-1);
			else
			   contents(end).name = filename;
			end
			catalog(cc).contents = contents;
			catalog(cc).code = code;
			%
			% Model structure depth
			%
			catalog(cc).depth = treedepth(cat(1,contents,contents(end)),size(contents,1),0,0);
		     end
		     versions = [mxVer;sbVer]/100;	   % Version numbers
		     sbhndlmsgs('write',' Done.','newline');
		  end
	       end
	    end
	 end
      end
   end
   %
   % If error encountered, return empty arguments
   %
   if (~isempty(msg))
      catalog = [];
      code = [];
      ws = [];
      depth = [];
      versions = [];
   end
   return					   	   % Last card od READSB
   
function [bstr,bint,bflt,bb] = mblksep(nBlocks,cstr,cint,cflt,newver)
%MBLKSEP
%
%
   if (newver)
      if (cint(9) == 13)
	 sbOffset = 5;
      elseif (cint(9) == 11)
	 sbOffset = 3;
      else
	 sbOffset = 0;
      end
   else
      sbOffset = 0;
   end
   %
   % Allocate memory to hold chars, ints and floats
   %
   bkStr = cell(1,nBlocks);
   bkInt = cell(1,nBlocks);
   bkFlt = cell(1,nBlocks);
   bstr = struct('sb','','blk','');
   bint = struct('sb',[],'blk',[]);
   bflt = struct('sb',[],'blk',[]);
   bbox = zeros(nBlocks+1,4);			   % Super block include
   %
   % Find where the ints, chars and floats are
   %
   iOffset = cint(18+sbOffset);
   fOffset = cint(19+sbOffset);
   sOffset = cint(20+sbOffset);
   iub = iOffset+cint(iOffset+1);
   fub = fOffset+cint(iOffset+2);
   sub = sOffset+cint(iOffset+3);
   rectInd = 2:5;				   % Block position indices
   bind = 1:nBlocks;
   for (cb = bind)				   % For each block do
      ilb = iub+1;
      flb = fub+1;
      slb = sub+1;
      if (cb == nBlocks)
	 iub = cint(21+sbOffset);
	 fub = cint(22+sbOffset);
	 sub = cint(23+sbOffset);
      else
	 bOffset = 3*cb;
	 iub = iOffset+cint(iOffset+bOffset+1);
	 fub = fOffset+cint(iOffset+bOffset+2);
	 sub = sOffset+cint(iOffset+bOffset+3);
      end
      blockInt = cint(ilb:iub);
      if (newver)
	 if (blockInt(9) == 13)
	    bkOffset = 5;
	 elseif (blockInt(9) == 11)
	    bkOffset = 3;
	 else
	    bkOffset = 0;
	 end
      else
	 bkOffset = 0;
      end
      blockInt(9:8+bkOffset) = [];
      ind = 9:3:blockInt(9)-bkOffset;
      blockInt(ind) = blockInt(ind)-bkOffset;
      bkInt{cb} = blockInt;
      bkFlt{cb} = cflt(flb:fub);
      bkStr{cb} = cstr(slb:sub);
      bbox(cb,:) = blockInt(blockInt(12)+rectInd);
   end
   %
   % Super block info
   %
   bstr.sb = {cstr(1:sOffset),cstr((sub+1):end)};
   bstr.blk = bkStr;
   bflt.sb = {cflt(1:fOffset),cflt((fub+1):end)};
   bflt.blk = bkFlt;
   blockInt = cint(1:iOffset);
   blockInt(9:8+sbOffset) = [];
   ind = 9:3:blockInt(9)-sbOffset;
   blockInt(ind) = blockInt(ind)-sbOffset;
   bint.sb = {blockInt,cint((iub+1):end)};
   bint.blk = bkInt;
   %
   % Bounding box calculation
   %
   bbox(bind,2:4) = [bbox(bind,2)+bbox(bind,4) bbox(bind,1)+bbox(bind,3) bbox(bind,2)];
   margin = 120;
   bbox(end,:) = [min(bbox(bind,1))-margin max(bbox(bind,2:3),[],1)+margin min(bbox(bind,4))-margin];
   %
   % Scale and remap the box positions
   %
   scal = 1;
   bbox(:,[1 3 2 4]) = scal*[bbox(:,[1 3])-bbox(end,1) bbox(end,2)-bbox(:,[2 4])];
   bb = struct('sb',bbox(end,:),'blk',bbox(bind,:));
   return					   % Last card of MBLKSEP

function t = cellcat(c,d)
%CELLCAT
%
%

   t = cell(size(c));
   nt = size(t,1);
   for (k = 1:nt)
      t{k} = cat(2,c{k},d);
   end
   return

function depth = treedepth(tree,node,depth,level)
%TREEDEPTH Returns the model structure depth
%
%

   %
   % At non-leaf node, increment the level
   %
   kids = tree(node).sbChild;
   if (~isempty(kids))
      nch = size(kids,2);
      for (ch = 1:nch)
	 depth = treedepth(tree,kids(ch),depth,level+1);
      end
   end
   parents = tree(node).bkChild;
   if (~isempty(parents))
      sibling = tree(node).sibling;
      npr = size(parents,2);
      for (pr = 1:npr)
	 kids = parents{pr};
	 if (sibling(pr))
	    tree(end).sbChild = kids;		   % Overlay the tree entry
	    depth = treedepth(tree,size(tree,1),depth,level+1);
	 else
	    nch = size(kids,2);
	    for (ch = 1:nch)
	       depth = treedepth(tree,kids(ch),depth,level+1);
	    end
	 end
      end
   end
   depth = max(depth,level);
   return					   % Last card of TREEDEPTH
