/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.codesourcery.jasm16.ast;
import java.io.IOException;
import java.io.InputStream;
import org.apache.log4j.Logger;
import de.codesourcery.jasm16.compiler.ICompilationContext;
import de.codesourcery.jasm16.compiler.io.IObjectCodeWriter;
import de.codesourcery.jasm16.compiler.io.IResource;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.exceptions.ResourceNotFoundException;
import de.codesourcery.jasm16.lexer.IToken;
import de.codesourcery.jasm16.lexer.TokenType;
import de.codesourcery.jasm16.parser.IParseContext;
/**
* '.incbin' AST node.
*
* @author tobias.gierke@code-sourcery.de
*/
public class IncludeBinaryFileNode extends ObjectCodeOutputNode implements IPreprocessorDirective
{
private static final Logger LOG = Logger.getLogger(IncludeBinaryFileNode.class);
private IResource resource;
private String resourceIdentifier;
private int resourceSize = UNKNOWN_SIZE;
@Override
protected ASTNode copySingleNode()
{
final IncludeBinaryFileNode result= new IncludeBinaryFileNode();
result.resourceIdentifier = resourceIdentifier;
result.setAddress( getAddress() );
result.resource = resource;
result.resourceSize = resourceSize;
return result;
}
@Override
public boolean supportsChildNodes()
{
return false;
}
@Override
protected ASTNode parseInternal(IParseContext context) throws ParseException
{
mergeWithAllTokensTextRegion( context.read( TokenType.INCLUDE_BINARY ) );
mergeWithAllTokensTextRegion( context.read( TokenType.WHITESPACE) );
mergeWithAllTokensTextRegion( context.read( "Expected a filename enclosed in string delimiters but no delimiter found" , TokenType.STRING_DELIMITER) );
final IToken tok = context.read( TokenType.CHARACTERS );
mergeWithAllTokensTextRegion( tok );
resourceIdentifier = tok.getContents();
try {
this.resource = context.resolveRelative( resourceIdentifier , context.getCompilationUnit().getResource() );
final long size = resource.getAvailableBytes();
if ( size < 0 || size > Integer.MAX_VALUE ) {
throw new RuntimeException("Internal error, resource "+resource+" returned size that does not fit into an Integer");
}
this.resourceSize = (int) size;
} catch(IOException e) {
LOG.error("parseInternal(): Failed to look up resource '"+resourceIdentifier+"'",e);
throw new ParseException("File \""+tok.getContents()+"\" does not exist" , tok );
} catch (ResourceNotFoundException e) {
throw new ParseException("File \""+tok.getContents()+"\" does not exist" , tok );
}
mergeWithAllTokensTextRegion(context.read( "Missing string delimiter at end of filename" , TokenType.STRING_DELIMITER) );
return this;
}
@Override
public void symbolsResolved(ICompilationContext context)
{
// nothing to do
}
@Override
public int getSizeInBytes(long thisNodesObjectCodeOffsetInBytes)
{
return resourceSize;
}
@Override
public void writeObjectCode(IObjectCodeWriter writer, ICompilationContext compContext) throws IOException, ParseException
{
setAddress( writer.getCurrentWriteOffset() );
final InputStream inputStream = resource.createInputStream();
try {
byte[] buffer = new byte[1024];
int len=0;
while ( ( len = inputStream.read( buffer ) ) > 0 ) {
writer.writeObjectCode( buffer , 0 , len );
}
} finally {
inputStream.close();
}
}
}