UEFI开发实战用户交互界面使用说明UNI文件
发布日期:2025-01-04 17:27 点击次数:133综述 UEFI用户交互界面的实现涉及到多种不同类型的文件,这里要讲的是UNI文件,它也是其中最简单的一种。本文主要参考自《edk-ii-uni-specification.pdf》(以下简称参考文档)。它可以在EDK II Specifications · tianocore/tianocore.github.io Wiki · GitHub下载到。文本的代码示例来自EDK2017,由于版本更新等原因,示例中的代码可能跟实际GIT库中的代码有一定的差异。 作用 关于UNI文件的作用,在参考文档中做了如下的说明: 就是说,UNI提供了一系列的标记(Token)用来表示文本,这些文本可以有不同的实现(比如可以使用不同的语言,不同的编码格式),但是在代码调用中都可以使用统一的标记来表示。以之前在UEFI用户交互界面使用说明中的图示为例: 这里的Select Language就是一个在UNI文件中定义的标记(具体在FrontPageStrings.uni文件中): 可以看到,这里实际上支持两种语言,一种是英语一种是法语,所以我们的Front Page中可以进行切换: 而在我们的代码中,实际上使用的是STR_LANGUAGE_SELECT这个标记: 至于具体如何切换,这里暂不深入。 语法 关于UNI文件中的语法,在参考文档中做了比较详细的说明(也有没说明白的地方)。但是因为使用了BNF的表示方法(包括BNF的扩展方法),所以初看起来还是有一些吃力,这里做简要的介绍。关于BNF以及它的扩展版本,可以通过链接查看,或者在其它的地方找到具体的使用说明。 基本定义 首先是一些需要了解的基本定义: UNI文件中需要指明LanguageCodes,它是表示语言的标记,比如美式英语是en-US,法语是fr-FR,中文是zh-CN等等,它们在rfc4646中定义。 UNI文件中使用的字符串可以包含EscChar,全称是Escape Character,中文翻译是转义字符,基本听到中文名懂编程的都知道是什么意思了。 UNI文件中使用的标记可以包含数组,英文大小写,下划线(_)和折线(-)。UNI文件中可以包含其它的文件,使用include表达式。还有在UNI文件中会用到的基本表达式的BNF表示: 简单来说,::=左侧的是表达式,右侧是字符串定义。比如<US>这个表达式是最基本的,它就表示一个空格,它也会在::=的右侧用到,来组成更复杂的标记;比如<MS>,它的定义是<US>+,就表示多个空格;再比如<ME>,它的定义是<MS>{<EOL>},<EOL>表示的是End Of Line,所以<ME>表达式实际上就是多个空格组成的一行。 以此类推,构成了UNI(其实也是其它一切用BNF或者它的扩展版本表示的)文件的语法基础,正是上述的基本语法阐述了一个UNI文件应该如何实现。 语法说明 UNI文件的基本语法如下: 以一个实际的例子做对比: 这里就可以很明确的看到各个部分。不过上述例子中还有一部分没有包含进去,就是 /=# 它应该属于<Content>的一部分,称为ControlRefactor,不过目前不清楚它的作用,并且不是所有的UNI文件中都包含它,另外<LanguageDefs>也不是所有的UNI文件都包含,关于这些,在参考文档中并没有特别说明原因。 <CommentLine>和<LanguageDefs>两部分都比较简单,这里主要说明下<Content>,它的定义如下: 1. 它也可以包含<CommentLine>,事实上注释可以出现在任何的位置,关于注释的表达式,在之前的基本定义中已经说明过,就是以//开头的行; 2. 空白行也随时可以出现; 3. <UnicodeLines>是以#string开头的一个字符串(中间可以换行),它算是<Content>中最重要的部分,包含了真正用来定义并在其它文件中使用的标记; 4. <ControlRefactor>在前面的红色字体中已有说明,含义不明; 5. <LanguageDefs>也可以出现在<Content>中; 6. <SecurityLines>同样含义不明; 7. <IncludeLines>也在之前提到过,就是用来包含其它UNI文件的,像下面那样: 上述<Content>的定义中,除了两个标红的不知道作用,最重要的就是<UnicodeLines>了,它的定义如下: 下面是一个具体的例子: 对于<FontId>没有找到具体的例子。这里说明下它的定义: 然后定义的Identifier就会被用在<UnicodeLines>中。 UNI文件的使用 本文的最后介绍下UNI文件的使用。 首先在C代码中要使用UNI文件,对应模块的INF文件中需要包含该文件,以Front Page对应的UiApp.inf为例: 在模块编译的过程中,编译工具(其实是Python脚本)将它们转换成对应的Unicode字节,放到AutoGen.c文件中的某个数组中。然后在C代码中要使用的话,需要先包含该UNI,使用的函数如下: 该函数将UNI文件提供的字符串最终放到HII数据库中,该HII数据库中还包含图像,窗体(Form),字体等内容。下面是一个具体的例子: 这里的UiAppStrings参数对应的就是UiApp.uni,对应关系是“BASE_NAME”+Strings对应到字符数组,这里的BASE_NAME的值来自inf。同时xxxStrings也是AutoGen.c中转换后得到的数组变量名。以上述的UiAppStrings为例(这里截取部分代码): 在UiApp.uni中定义的第一个标记是: 在上述的字节中(PACKAGE DATA部分),第一个0x14有其它的含义(见EFI_HII_SIBT_STRING_UCS2),后面的每两个字节就表示一个Unicode字符的英文或者法文(上图是英文,法文在数组的后面,这里没有列出),可以看到0x0045表示的是Unicode的E,0x006E表示的是Unicode的n(具体的转换可以看这里),以此类推。 之后就可以通过对应的GUID使用到定义在UNI中的标记了,使用的方式是 STRING_TOKEN (标记) 这里的标记最终会被定义成一个宏,放到跟AutoGen.c同一层目录下的xxxStrDefs.h文件中,这里的xxx就是模块的名称。这个STRING_TOKEN(标记)会被当成一个宏,对应到一个整数,通过这个整数可以找到上述数组中对应Unicode字符串的位置。这个宏最终会被EFI_HII_STRING_PROTOCOL中的GetString()函数使用并解析成字符串: 该Protocol在《UEFI Spec》中也有定义。它的初始化位于HiiDatabaseDxe.inf模块中,具体的实现不在这里深入了。 以上就是UNI文件说明的全部内容,更多关于UEFI用户交互界面UNI文件的资料请关注脚本之家其它相关文章! 您可能感兴趣的文章:UEFI开发实战用户交互界面基础说明UEFI开发实战用户交互界面使用说明VFR文件UEFI开发基础HII代码示例UEFI开发基础汇编代码的使用UEFI开发实战SlimBootloader中调用FSP