mirror of
https://gitlab.com/c3d/db48x.git
synced 2024-09-29 05:36:58 +02:00
Move DMCP file operations to separate file.h / file.cc
This will be useful if we want to do more than just reading the on-line help. Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
parent
1d58abd64c
commit
39be6cc0c2
6 changed files with 315 additions and 223 deletions
1
Makefile
1
Makefile
|
@ -99,6 +99,7 @@ CXX_SOURCES += \
|
||||||
src/dm42/sysmenu.cc \
|
src/dm42/sysmenu.cc \
|
||||||
src/dm42/main.cc \
|
src/dm42/main.cc \
|
||||||
src/input.cc \
|
src/input.cc \
|
||||||
|
src/file.cc \
|
||||||
src/stack.cc \
|
src/stack.cc \
|
||||||
src/util.cc \
|
src/util.cc \
|
||||||
src/renderer.cc \
|
src/renderer.cc \
|
||||||
|
|
|
@ -39,6 +39,7 @@ SOURCES += \
|
||||||
../src/util.cc \
|
../src/util.cc \
|
||||||
../src/renderer.cc \
|
../src/renderer.cc \
|
||||||
../src/input.cc \
|
../src/input.cc \
|
||||||
|
../src/file.cc \
|
||||||
../src/stack.cc \
|
../src/stack.cc \
|
||||||
../src/settings.cc \
|
../src/settings.cc \
|
||||||
../src/object.cc \
|
../src/object.cc \
|
||||||
|
|
150
src/file.cc
Normal file
150
src/file.cc
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
// ****************************************************************************
|
||||||
|
// file.cc DB48X project
|
||||||
|
// ****************************************************************************
|
||||||
|
//
|
||||||
|
// File Description:
|
||||||
|
//
|
||||||
|
// Abstract interface for the zany DMCP filesystem
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// ****************************************************************************
|
||||||
|
// (C) 2023 Christophe de Dinechin <christophe@dinechin.org>
|
||||||
|
// This software is licensed under the terms outlined in LICENSE.txt
|
||||||
|
// ****************************************************************************
|
||||||
|
// This file is part of DB48X.
|
||||||
|
//
|
||||||
|
// DB48X is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms outlined in the LICENSE.txt file
|
||||||
|
//
|
||||||
|
// DB48X is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
// ****************************************************************************
|
||||||
|
|
||||||
|
#include "file.h"
|
||||||
|
#include "recorder.h"
|
||||||
|
|
||||||
|
RECORDER(file, 16, "File operations");
|
||||||
|
RECORDER(file_error, 16, "File errors");
|
||||||
|
|
||||||
|
|
||||||
|
file::file()
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Construct a file object
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
: data()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
file::~file()
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Close the help file
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void file::open(cstring path)
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Open a help file
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
#if SIMULATOR
|
||||||
|
data = fopen(path, "r");
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
record(file_error, "Error %s opening %s", strerror(errno), path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
FRESULT ok = f_open(&data, path, FA_READ);
|
||||||
|
if (ok != FR_OK)
|
||||||
|
{
|
||||||
|
data.obj.objsize = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif // SIMULATOR
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void file::close()
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Close the help file
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
if (valid())
|
||||||
|
fclose(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unicode file::get()
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Read UTF8 code at offset
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
unicode code = valid() ? fgetc(data) : unicode(EOF);
|
||||||
|
if (code == unicode(EOF))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (code & 0x80)
|
||||||
|
{
|
||||||
|
// Reference: Wikipedia UTF-8 description
|
||||||
|
if ((code & 0xE0) == 0xC0)
|
||||||
|
code = ((code & 0x1F) << 6)
|
||||||
|
| (fgetc(data) & 0x3F);
|
||||||
|
else if ((code & 0xF0) == 0xE0)
|
||||||
|
code = ((code & 0xF) << 12)
|
||||||
|
| ((fgetc(data) & 0x3F) << 6)
|
||||||
|
| (fgetc(data) & 0x3F);
|
||||||
|
else if ((code & 0xF8) == 0xF0)
|
||||||
|
code = ((code & 0xF) << 18)
|
||||||
|
| ((fgetc(data) & 0x3F) << 12)
|
||||||
|
| ((fgetc(data) & 0x3F) << 6)
|
||||||
|
| (fgetc(data) & 0x3F);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint file::find(unicode cp)
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Find a given code point in file looking forward
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Return position right before code point, position file right after it
|
||||||
|
{
|
||||||
|
unicode c;
|
||||||
|
uint off;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
off = ftell(data);
|
||||||
|
c = get();
|
||||||
|
} while (c && c != cp);
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint file::rfind(unicode cp)
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Find a given code point in file looking backward
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Return position right before code point, position file right after it
|
||||||
|
{
|
||||||
|
uint off = ftell(data);
|
||||||
|
unicode c;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (off == 0)
|
||||||
|
break;
|
||||||
|
fseek(data, --off, SEEK_SET);
|
||||||
|
c = get();
|
||||||
|
}
|
||||||
|
while (c != cp);
|
||||||
|
return off;
|
||||||
|
}
|
152
src/file.h
Normal file
152
src/file.h
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
#ifndef FILE_H
|
||||||
|
# define FILE_H
|
||||||
|
// ****************************************************************************
|
||||||
|
// file.h DB48X project
|
||||||
|
// ****************************************************************************
|
||||||
|
//
|
||||||
|
// File Description:
|
||||||
|
//
|
||||||
|
// Abstract the DMCP zany filesystem interface
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// ****************************************************************************
|
||||||
|
// (C) 2023 Christophe de Dinechin <christophe@dinechin.org>
|
||||||
|
// This software is licensed under the terms outlined in LICENSE.txt
|
||||||
|
// ****************************************************************************
|
||||||
|
// This file is part of DB48X.
|
||||||
|
//
|
||||||
|
// DB48X is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms outlined in the LICENSE.txt file
|
||||||
|
//
|
||||||
|
// DB48X is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
// ****************************************************************************
|
||||||
|
|
||||||
|
#include "dmcp.h"
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct file
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Direct access to the help file
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
file();
|
||||||
|
~file();
|
||||||
|
|
||||||
|
void open(cstring path);
|
||||||
|
bool valid();
|
||||||
|
void close();
|
||||||
|
unicode get();
|
||||||
|
unicode get(uint offset);
|
||||||
|
void seek(uint offset);
|
||||||
|
unicode peek();
|
||||||
|
uint position();
|
||||||
|
uint find(unicode cp);
|
||||||
|
uint rfind(unicode cp);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
#if SIMULATOR
|
||||||
|
FILE *data;
|
||||||
|
#else
|
||||||
|
FIL data;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DMCP wrappers
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SIMULATOR
|
||||||
|
#define ftell(f) f_tell(&f)
|
||||||
|
#define fseek(f,o,w) f_lseek(&f,o)
|
||||||
|
#define fclose(f) f_close(&f)
|
||||||
|
|
||||||
|
static inline int fgetc(FIL &f)
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Read one character from a file - Wrapper for DMCP filesystem
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
UINT br = 0;
|
||||||
|
char c = 0;
|
||||||
|
if (f_read(&f, &c, 1, &br) != FR_OK || br != 1)
|
||||||
|
return EOF;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
#endif // SIMULATOR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Inline functions for simple stuff
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
inline bool file::valid()
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Return true if the input file is OK
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
#if SIMULATOR
|
||||||
|
return data != 0;
|
||||||
|
#else
|
||||||
|
return f_size(&data) != 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void file::seek(uint off)
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Move the read position in the data file
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
fseek(data, off, SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline unicode file::peek()
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Look at what is as current position without moving it
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
uint off = ftell(data);
|
||||||
|
unicode result = get();
|
||||||
|
seek(off);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline unicode file::get(uint off)
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Get code point at given offset
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
seek(off);
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline uint file::position()
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Return current position in help file
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
{
|
||||||
|
return ftell(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FILE_H
|
186
src/input.cc
186
src/input.cc
|
@ -1175,192 +1175,6 @@ void input::draw_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
input::file::file()
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Construct a file object
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
: data()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
input::file::~file()
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Close the help file
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void input::file::open(cstring path)
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Open a help file
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
#if SIMULATOR
|
|
||||||
data = fopen(path, "r");
|
|
||||||
if (!data)
|
|
||||||
{
|
|
||||||
record(help, "Error %s opening %s", strerror(errno), path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
FRESULT ok = f_open(&data, path, FA_READ);
|
|
||||||
if (ok != FR_OK)
|
|
||||||
{
|
|
||||||
data.obj.objsize = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#define ftell(f) f_tell(&f)
|
|
||||||
#define fseek(f,o,w) f_lseek(&f,o)
|
|
||||||
#define fclose(f) f_close(&f)
|
|
||||||
#endif // SIMULATOR
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void input::file::close()
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Close the help file
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
if (valid())
|
|
||||||
fclose(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline bool input::file::valid()
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Return true if the input file is OK
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
#if SIMULATOR
|
|
||||||
return data != 0;
|
|
||||||
#else
|
|
||||||
return f_size(&data) != 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef SIMULATOR
|
|
||||||
static inline int fgetc(FIL &f)
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Read one character from a file - Wrapper for DMCP filesystem
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
UINT br = 0;
|
|
||||||
char c = 0;
|
|
||||||
if (f_read(&f, &c, 1, &br) != FR_OK || br != 1)
|
|
||||||
return EOF;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
#endif // SIMULATOR
|
|
||||||
|
|
||||||
|
|
||||||
unicode input::file::get()
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Read UTF8 code at offset
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
unicode code = valid() ? fgetc(data) : unicode(EOF);
|
|
||||||
if (code == unicode(EOF))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (code & 0x80)
|
|
||||||
{
|
|
||||||
// Reference: Wikipedia UTF-8 description
|
|
||||||
if ((code & 0xE0) == 0xC0)
|
|
||||||
code = ((code & 0x1F) << 6)
|
|
||||||
| (fgetc(data) & 0x3F);
|
|
||||||
else if ((code & 0xF0) == 0xE0)
|
|
||||||
code = ((code & 0xF) << 12)
|
|
||||||
| ((fgetc(data) & 0x3F) << 6)
|
|
||||||
| (fgetc(data) & 0x3F);
|
|
||||||
else if ((code & 0xF8) == 0xF0)
|
|
||||||
code = ((code & 0xF) << 18)
|
|
||||||
| ((fgetc(data) & 0x3F) << 12)
|
|
||||||
| ((fgetc(data) & 0x3F) << 6)
|
|
||||||
| (fgetc(data) & 0x3F);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void input::file::seek(uint off)
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Move the read position in the data file
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
fseek(data, off, SEEK_SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline unicode input::file::peek()
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Look at what is as current position without moving it
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
uint off = ftell(data);
|
|
||||||
unicode result = get();
|
|
||||||
seek(off);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline unicode input::file::get(uint off)
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Get code point at given offset
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
seek(off);
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline uint input::file::position()
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Return current position in help file
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
return ftell(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline uint input::file::find(unicode cp)
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Find a given code point in file looking forward
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Return position right before code point, position file right after it
|
|
||||||
{
|
|
||||||
unicode c;
|
|
||||||
uint off;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
off = ftell(data);
|
|
||||||
c = get();
|
|
||||||
} while (c && c != cp);
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline uint input::file::rfind(unicode cp)
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Find a given code point in file looking backward
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Return position right before code point, position file right after it
|
|
||||||
{
|
|
||||||
uint off = ftell(data);
|
|
||||||
unicode c;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (off == 0)
|
|
||||||
break;
|
|
||||||
fseek(data, --off, SEEK_SET);
|
|
||||||
c = get();
|
|
||||||
}
|
|
||||||
while (c != cp);
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void input::load_help(utf8 topic)
|
void input::load_help(utf8 topic)
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
48
src/input.h
48
src/input.h
|
@ -29,10 +29,11 @@
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
|
|
||||||
|
#include "dmcp.h"
|
||||||
|
#include "file.h"
|
||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "dmcp.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -177,42 +178,15 @@ protected:
|
||||||
bool autoComplete : 1; // Menu is auto-complete
|
bool autoComplete : 1; // Menu is auto-complete
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Key mappings
|
// Key mappings
|
||||||
object_p function[NUM_PLANES][NUM_KEYS];
|
object_p function[NUM_PLANES][NUM_KEYS];
|
||||||
cstring menu_label[NUM_PLANES][NUM_SOFTKEYS];
|
cstring menu_label[NUM_PLANES][NUM_SOFTKEYS];
|
||||||
uint16_t menu_marker[NUM_PLANES][NUM_SOFTKEYS];
|
uint16_t menu_marker[NUM_PLANES][NUM_SOFTKEYS];
|
||||||
bool menu_marker_align[NUM_PLANES][NUM_SOFTKEYS];
|
bool menu_marker_align[NUM_PLANES][NUM_SOFTKEYS];
|
||||||
static runtime &RT;
|
file helpfile;
|
||||||
friend struct tests;
|
static runtime &RT;
|
||||||
friend struct runtime;
|
friend struct tests;
|
||||||
|
friend struct runtime;
|
||||||
protected:
|
|
||||||
struct file
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Direct access to the help file
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
{
|
|
||||||
file();
|
|
||||||
~file();
|
|
||||||
|
|
||||||
void open(cstring path);
|
|
||||||
bool valid();
|
|
||||||
void close();
|
|
||||||
unicode get();
|
|
||||||
unicode get(uint offset);
|
|
||||||
void seek(uint offset);
|
|
||||||
unicode peek();
|
|
||||||
uint position();
|
|
||||||
uint find(unicode cp);
|
|
||||||
uint rfind(unicode cp);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
#ifdef SIMULATOR
|
|
||||||
FILE *data;
|
|
||||||
#else
|
|
||||||
FIL data;
|
|
||||||
#endif
|
|
||||||
} helpfile;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue