lvgl - 移植文件系統(tǒng)

LVGL 文件系統(tǒng)移植

文件系統(tǒng)介紹

在 LVGL 里可以將圖像以文件的形式存儲(chǔ)在 SPI Flash 或 SD 卡上噪叙,但是必須存儲(chǔ)在 LVGL 的文件系統(tǒng)里阳液,因此實(shí)踐移植了 LVGL 文件系統(tǒng)。

LVGL 的文件系統(tǒng)可以適配任何其他的文件系統(tǒng)咖为,只要實(shí)現(xiàn)相應(yīng)的回調(diào)函數(shù)即可秕狰。該文件系統(tǒng)有一個(gè)驅(qū)動(dòng)字符(drive letter)用于識(shí)別不同設(shè)備,例如 SD 卡可以與驅(qū)動(dòng)字符 'S' 相關(guān)聯(lián)躁染,這樣可以通過路徑 "S:/path/to/file.txt" 來訪問一個(gè)文件鸣哀。

添加驅(qū)動(dòng)

為了添加文件系統(tǒng)的驅(qū)動(dòng),需要初始化一個(gè) lv_fs_drv_t 的結(jié)構(gòu)體吞彤,類似下列示例代碼:

lv_fs_drv_t drv;
lv_fs_drv_init(&drv);                     /*Basic initialization*/

drv.letter = 'S';                         /*An uppercase letter to identify the drive */
drv.file_size = sizeof(my_file_object);   /*Size required to store a file object*/
drv.rddir_size = sizeof(my_dir_object);   /*Size required to store a directory object (used by dir_open/close/read)*/
drv.ready_cb = my_ready_cb;               /*Callback to tell if the drive is ready to use */
drv.open_cb = my_open_cb;                 /*Callback to open a file */
drv.close_cb = my_close_cb;               /*Callback to close a file */
drv.read_cb = my_read_cb;                 /*Callback to read a file */
drv.write_cb = my_write_cb;               /*Callback to write a file */
drv.seek_cb = my_seek_cb;                 /*Callback to seek in a file (Move cursor) */
drv.tell_cb = my_tell_cb;                 /*Callback to tell the cursor position  */
drv.trunc_cb = my_trunc_cb;               /*Callback to delete a file */
drv.size_cb = my_size_cb;                 /*Callback to tell a file's size */
drv.rename_cb = my_rename_cb;             /*Callback to rename a file */

drv.dir_open_cb = my_dir_open_cb;         /*Callback to open directory to read its content */
drv.dir_read_cb = my_dir_read_cb;         /*Callback to read a directory's content */
drv.dir_close_cb = my_dir_close_cb;       /*Callback to close a directory */

drv.free_space_cb = my_free_space_cb;     /*Callback to tell free space on the drive */

drv.user_data = my_user_data;             /*Any custom data if required*/

lv_fs_drv_register(&drv);                 /*Finally register the drive*/

上述的任何一個(gè)回調(diào)函數(shù)都可以為 NULL 表明該操作不支持我衬。

舉例說明一些這些回調(diào)函數(shù)是如何被使用的叹放,如果我們執(zhí)行操作:

lv_fs_open(&file, "S:/folder/file.txt", LV_FS_MODE_WR);

LVGL 會(huì)依次

  1. 檢查已經(jīng)注冊(cè)的驅(qū)動(dòng)其驅(qū)動(dòng)字節(jié)是否為 's'
  2. 檢查 open_cb 回調(diào)函數(shù)是否被實(shí)現(xiàn)(不為 NULL)挠羔。
  3. 調(diào)用 open_cb井仰,其中 path 參數(shù)為 "folder/file.txt"

使用示例

以下代碼演示了如何讀取文件:

lv_fs_file_t f;
lv_fs_res_t res;
res = lv_fs_open(&f, "S:folder/file.txt", LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) my_error_handling();

uint32_t read_num;
uint8_t buf[8];
res = lv_fs_read(&f, buf, 8, &read_num);
if(res != LV_FS_RES_OK || read_num != 8) my_error_handling();

lv_fs_close(&f);

lv_fs_open 里的 mode 參數(shù)也可以是 LV_FS_MODE_WRLV_FS_MODE_WR | LV_FS_MODE_RD 破加。

下面的例子展示了如何讀取一個(gè)目錄里的內(nèi)容俱恶。由驅(qū)動(dòng)程序決定如何標(biāo)記目錄,類似 Unix 風(fēng)格范舀,最好在目錄名稱前面插入 '/' 表明根目錄合是。

lv_fs_dir_t dir;
lv_fs_res_t res;
res = lv_fs_dir_open(&dir, "S:/folder");
if(res != LV_FS_RES_OK) my_error_handling();

char fn[256];
while(1) {
    res = lv_fs_dir_read(&dir, fn);
    if(res != LV_FS_RES_OK) {
        my_error_handling();
        break;
    }

    /*fn is empty, if not more files to read*/
    if(strlen(fn) == 0) {
        break;
    }

    printf("%s\n", fn);
}

lv_fs_dir_close(&dir);

圖像所使用的驅(qū)動(dòng)程序

LVGL 的 image 對(duì)象可以從文件里打開(另一種方式是直接存儲(chǔ)在 Flash)。

為了初始化圖像锭环,需要以下回調(diào)函數(shù)的支持:

  • open_cb
  • close_cb
  • read_cb
  • seek_cb
  • tell_cb

移植

因?yàn)橐浦策@個(gè)文件系統(tǒng)主要是為了存儲(chǔ)并打開圖像文件聪全,因此沒必要完全實(shí)現(xiàn),只需實(shí)現(xiàn) open, close, read, seek, tell 這五個(gè)回調(diào)函數(shù)即可辅辩。

本次移植之前需要在 RT-Thread 里成功移植了 SD 卡难礼,并掛載了虛擬文件系統(tǒng) DFS,可以參考:RT-Thread Studio驅(qū)動(dòng)SD卡 汽久。

RT-Thread 的 DFS 實(shí)現(xiàn)了 POSIX 接口鹤竭,因此可以使用 POSIX 的 open, close, read, lseek 這些操作文件的 API踊餐,大大簡(jiǎn)化了移植 LVGL 文件系統(tǒng)的事件景醇。

這些接口的使用可以參考 RT-Thread 文檔

在 port 目錄下有一個(gè)官方提供的移植模板文件 lv_port_fs_template.c吝岭,這里面提供了移植的步驟三痰,以及需要實(shí)現(xiàn)的部分。

類型定義

模板文件里相關(guān)內(nèi)容如下:

/**********************
 *      TYPEDEFS
 **********************/

/* Create a type to store the required data about your file.
 * If you are using a File System library
 * it already should have a File type.
 * For example FatFS has `FIL`. In this case use `typedef FIL file_t`*/
typedef struct {
    /*Add the data you need to store about a file*/
    uint32_t dummy1;
    uint32_t dummy2;
}file_t;

/*Similarly to `file_t` create a type for directory reading too */
typedef struct {
    /*Add the data you need to store about directory reading*/
    uint32_t dummy1;
    uint32_t dummy2;
}dir_t;

根據(jù)提示窜管,file_t 是一個(gè)類似句柄的類型散劫,就比如 FatFS 里的 FIL 以及 POSIX 里的 int 。而且我們目前只需要操作文件幕帆,不需要操作目錄获搏,因此上述內(nèi)容就可以更改為:

typedef int file_t;

函數(shù)申明

其次是函數(shù)聲明的部分:

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void fs_init(void);

static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos);
static lv_fs_res_t fs_size (lv_fs_drv_t * drv, void * file_p, uint32_t * size_p);
static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static lv_fs_res_t fs_remove (lv_fs_drv_t * drv, const char *path);
static lv_fs_res_t fs_trunc (lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const char * newname);
static lv_fs_res_t fs_free (lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p);
static lv_fs_res_t fs_dir_open (lv_fs_drv_t * drv, void * rddir_p, const char *path);
static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * rddir_p, char *fn);
static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * rddir_p);

較老版本的移植模板文件這些函數(shù)可能沒有第一個(gè)參數(shù) lv_fs_drv_t * drv,需要自己添加失乾。

刪除不必要的接口常熙,只保留我們需要的部分,如下所示:

static void fs_init(void);

static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos);
static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);

初始化函數(shù)

void lv_port_fs_init(void)
{
    /*----------------------------------------------------
     * Initialize your storage device and File System
     * -------------------------------------------------*/
    fs_init();

    /*---------------------------------------------------
     * Register the file system interface  in LittlevGL
     *--------------------------------------------------*/

    /* Add a simple drive to open images */
    lv_fs_drv_t fs_drv;                         /*A driver descriptor*/
    memset(&fs_drv, 0, sizeof(lv_fs_drv_t));    /*Initialization*/

    /*Set up fields...*/
    fs_drv.file_size = sizeof(file_t);
    fs_drv.letter = 'P';
    fs_drv.open = fs_open;
    fs_drv.close = fs_close;
    fs_drv.read = fs_read;
    fs_drv.write = fs_write;
    fs_drv.seek = fs_seek;
    fs_drv.tell = fs_tell;
    fs_drv.free = fs_free;
    fs_drv.size = fs_size;
    fs_drv.remove = fs_remove;
    fs_drv.rename = fs_rename;
    fs_drv.trunc = fs_trunc;

    fs_drv.rddir_size = sizeof(dir_t);
    fs_drv.dir_close = fs_dir_close;
    fs_drv.dir_open = fs_dir_open;
    fs_drv.dir_read = fs_dir_read;

    lv_fs_add_drv(&fs_drv);
}

初始化函數(shù)這里會(huì)注冊(cè)設(shè)備碱茁,設(shè)備描述符 fs_drv 經(jīng)過默認(rèn)初始化后所有的回調(diào)函數(shù)都為 NULL裸卫,因此沒有回調(diào)函數(shù)的成員可以不用管。修改成如下內(nèi)容:

int lv_port_fs_init(void)
{
    /*----------------------------------------------------
     * Initialize your storage device and File System
     * -------------------------------------------------*/
    fs_init();

    /*---------------------------------------------------
     * Register the file system interface  in LittlevGL
     *--------------------------------------------------*/

    /* Add a simple drive to open images */
    lv_fs_drv_t fs_drv;                         /*A driver descriptor*/
    lv_fs_drv_init(&fs_drv);                    /*Basic initialization*/

    /*Set up fields...*/
    fs_drv.file_size = sizeof(file_t);
    fs_drv.letter = 'S';
    fs_drv.open_cb = fs_open;
    fs_drv.close_cb = fs_close;
    fs_drv.read_cb = fs_read;
    fs_drv.seek_cb = fs_seek;
    fs_drv.tell_cb = fs_tell;

    lv_fs_drv_register(&fs_drv);

    return RT_EOK;
}

這個(gè)初始化函數(shù)需要在 LVGL 初始化之后再調(diào)用纽竣。

回調(diào)接口實(shí)現(xiàn)

fs_init

該函數(shù)本來是為了用戶初始化 SD 卡或 Flash墓贿,但我們的 SD 卡或 Flash 在外部已經(jīng)初始化好了茧泪,因此這里默認(rèn)不管即可。

fs_open

在這個(gè)接口里我們需要按照 mode 模式打開文件聋袋,由于我們只需要讀文件队伟,因此只實(shí)現(xiàn) LV_FS_MODE_RD 的部分即可,模板文件內(nèi)容如下:

/**
 * Open a file
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable
 * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
 * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    if(mode == LV_FS_MODE_WR)
    {
        /*Open a file for write*/

        /* Add your code here*/
    }
    else if(mode == LV_FS_MODE_RD)
    {
        /*Open a file for read*/

        /* Add your code here*/
    }
    else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD))
    {
        /*Open a file for read and write*/

        /* Add your code here*/
    }

    return res;
}

函數(shù)參數(shù) file_p 就是之前定義的 file_t 類型的指針幽勒,在實(shí)際移植過程中需要用相應(yīng)的句柄去填充 file_p 所指向的內(nèi)容缰泡,因?yàn)楹竺鎸?shí)現(xiàn)其他接口時(shí)都是通過 file_p 這個(gè)參數(shù)來傳遞句柄。這里我填充了 POSIX 的 int 類型句柄:

int fd;
*(file_t *)file_p = fd;

一般 SD 卡或 Flash 都是掛載在 '/' 目錄下的代嗤,為了與其他內(nèi)容區(qū)分開棘钞,我這里將 LVGL 打開文件的起始地址導(dǎo)向了 '/lvgl/' 目錄下,也就是說 lv_fs_open(&f, "S:/file.txt", LV_FS_MODE_RD); 實(shí)際是調(diào)用了 open("/lvgl/file.txt", O_RDONLY) 這個(gè)函數(shù)干毅。將 LVGL 的默認(rèn)目錄定義為 '/lvgl/'宜猜,也可以理解為將 LVGL 的文件系統(tǒng)掛載到 '/lvgl/' 目錄下了。

移植后內(nèi)容如下:

static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;
    
    rt_size_t len = rt_strlen(path);
    RT_ASSERT(len < DFS_PATH_MAX);

    char posix_path[DFS_PATH_MAX] = "/lvgl/";
    const char *lvgl_dir_path = path;
    strncat(posix_path, lvgl_dir_path, DFS_PATH_MAX-6);

    int fd;
    if(mode == LV_FS_MODE_RD)
    {
        /*Open a file for read*/
        if ((fd = open(posix_path, O_RDONLY)) > 0) {
            *(file_t *)file_p = fd;
            res = LV_FS_RES_OK;         // open success
        } else {
            res = LV_FS_RES_NOT_EX;     // open fail
        }
    }

    return res;
}

fs_close

模板示例如下:

/**
 * Close an opened file
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable. (opened with lv_ufs_open)
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /* Add your code here*/

    return res;
}

這個(gè)接口需要關(guān)閉打開的文件硝逢,因此直接調(diào)用 close 即可姨拥,用 int fd = *(file_t *)file_pfile_p 強(qiáng)制轉(zhuǎn)換為 POSIX 的 int 型句柄,移植如下:

static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p)
{
    lv_fs_res_t res = LV_FS_RES_UNKNOWN;

    int fd = *(file_t *)file_p;
    if (close(fd) == 0)
        res = LV_FS_RES_OK;         // close success

    return res;
}

fs_read

模板示例如下:

/**
 * Read data from an opened file
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable.
 * @param buf pointer to a memory block where to store the read data
 * @param btr number of Bytes To Read
 * @param br the real number of read bytes (Byte Read)
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /* Add your code here*/

    return res;
}

需要從文件里讀出最大 btr 個(gè)字節(jié)到緩沖區(qū) buf 里渠鸽,實(shí)際讀取的字節(jié)通過 br 返回叫乌。移植如下:

static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
    lv_fs_res_t res = LV_FS_RES_UNKNOWN;

    int fd = *(file_t *)file_p;
    int read_bytes = read(fd, buf, btr);
    if (read_bytes >= 0) {
        *br = read_bytes;
        res = LV_FS_RES_OK;
    }
    
    return res;
}

fs_seek

模板示例如下:

/**
 * Set the read write pointer. Also expand the file size if necessary.
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable. (opened with lv_ufs_open )
 * @param pos the new position of read write pointer
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /* Add your code here*/

    return res;
}

該函數(shù)將文件的讀寫流移動(dòng)到距文件開始的 pos 個(gè)字節(jié)處。很容易用 lseek 實(shí)現(xiàn):

static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos)
{
    lv_fs_res_t res = LV_FS_RES_UNKNOWN;

    int fd = *(file_t *)file_p;
    if (lseek(fd, pos, SEEK_SET) >= 0)
        res = LV_FS_RES_OK;

    return res;
}

fs_tell

模板示例如下:

/**
 * Give the position of the read write pointer
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable.
 * @param pos_p pointer to to store the result
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /* Add your code here*/

    return res;
}

該函數(shù)需要得到文件當(dāng)前的讀寫流的位置徽缚,也可以用 lseek 來實(shí)現(xiàn):

static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
    lv_fs_res_t res = LV_FS_RES_UNKNOWN;

    int fd = *(file_t *)file_p;
    off_t pos = lseek(fd, 0, SEEK_CUR);
    if (pos >= 0) {
        *pos_p = pos;
        res = LV_FS_RES_OK;
    }

    return res;
}

測(cè)試

如何檢驗(yàn)我們是否移植成功呢憨奸?需要測(cè)試一下了。

我使用的測(cè)試代碼如下凿试,用到了上述所有回調(diào)接口排宰,初始化完成之后就可以調(diào)用進(jìn)行測(cè)試了。

注意:此時(shí)文件系統(tǒng)里需有 /lvgl/tmp.txt 這個(gè)文件那婉。

#include "lvgl/lvgl.h"

long lv_tell(lv_fs_file_t *fd)
{
    uint32_t pos = 0;
    lv_fs_tell(fd, &pos);
    rt_kprintf("\tcur pos is: %d\n", pos);
    return pos;
}

void lvgl_fs_test(void)
{
    char rbuf[30] = {0};
    uint32_t rsize = 0;
    lv_fs_file_t fd;
    lv_fs_res_t res;

    res = lv_fs_open(&fd, "S:/tmp.txt", LV_FS_MODE_RD);
    if (res != LV_FS_RES_OK) {
        rt_kprintf("open S:/tmp.txt ERROR\n");
        return ;
    }
    lv_tell(&fd);

    lv_fs_seek(&fd, 3);
    lv_tell(&fd);

    res = lv_fs_read(&fd, rbuf, 100, &rsize);
    if (res != LV_FS_RES_OK) {
        rt_kprintf("read ERROR\n");
        return ;
    }
    lv_tell(&fd);
    rt_kprintf("READ(%d): %s",rsize , rbuf);

    lv_fs_close(&fd);
}

可以進(jìn)行如下操作得到這個(gè)文件:

然后復(fù)位開發(fā)板板甘,可以看到如下現(xiàn)象:

此時(shí)說明文件系統(tǒng)移植成功。

完整的移植文件可以在我的 github 里找到详炬。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盐类,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子呛谜,更是在濱河造成了極大的恐慌在跳,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呻率,死亡現(xiàn)場(chǎng)離奇詭異硬毕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)礼仗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門吐咳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逻悠,“玉大人,你說我怎么就攤上這事韭脊⊥耍” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵沪羔,是天一觀的道長(zhǎng)饥伊。 經(jīng)常有香客問我,道長(zhǎng)蔫饰,這世上最難降的妖魔是什么琅豆? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮篓吁,結(jié)果婚禮上茫因,老公的妹妹穿的比我還像新娘。我一直安慰自己杖剪,他們只是感情好冻押,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盛嘿,像睡著了一般洛巢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上次兆,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天稿茉,我揣著相機(jī)與錄音,去河邊找鬼狈邑。 笑死城须,一個(gè)胖子當(dāng)著我的面吹牛蚤认,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糕伐,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼砰琢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了良瞧?” 一聲冷哼從身側(cè)響起陪汽,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褥蚯,沒想到半個(gè)月后挚冤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赞庶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年训挡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澳骤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澜薄,死狀恐怖为肮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肤京,我是刑警寧澤颊艳,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站忘分,受9級(jí)特大地震影響棋枕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妒峦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一戒悠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧舟山,春花似錦绸狐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至若债,卻和暖如春符相,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蠢琳。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工啊终, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人傲须。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓蓝牲,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親泰讽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子例衍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容